Я работаю над проектом, который требует автоматической c векторизации больших циклов. Для компиляции обязательно использовать G CC. Минимальный случай проблемы может быть следующим:
#define VLEN 4
#define NTHREADS 4
#define AVX512_ALIGNMENT 64
#define NUM_INTERNAL_ITERS 5
#define real double
typedef struct private_data {
/*
* Alloc enough space for private data and MEM_BLOCK_SIZE bytes of padding.
* Private data must be allocated all at once to squeeze cache performance by only
* padding once per CPU.
*/
real *contiguous_data;
/*
* Pointers to corresponding index in contiguous_data.
*/
real *array_1;
real *array_2;
} private_data_t;
private_data_t private_data[NTHREADS];
int num_iter;
void minimum_case(const int thread) {
// Reference to thread private data.
real *restrict array_1 =
__builtin_assume_aligned(private_data[thread].array_1, AVX512_ALIGNMENT);
real *restrict array_2 =
__builtin_assume_aligned(private_data[thread].array_2, AVX512_ALIGNMENT);
for (int i = 0; i < num_iter; i++) {
for (int k = 0; k < NUM_INTERNAL_ITERS; ++k) {
int array_1_entry =
(k * (NUM_INTERNAL_ITERS) * VLEN) +
i * NUM_INTERNAL_ITERS * NUM_INTERNAL_ITERS * VLEN;
int array_2_entry =
(k * (NUM_INTERNAL_ITERS) * VLEN) +
i * NUM_INTERNAL_ITERS * VLEN;
#pragma GCC unroll 1
#pragma GCC ivdep
for (int j = 0; j < VLEN; j++) {
real pivot;
int a_idx = array_1_entry + VLEN * 0 + j;
int b_idx = array_1_entry + VLEN * 1 + j;
int c_idx = array_1_entry + VLEN * 2 + j;
int d_idx = array_1_entry + VLEN * 3 + j;
int S_idx = array_2_entry + VLEN * 0 + j;
if (k == 0) {
pivot = array_1[a_idx];
// b = b / a
array_1[b_idx] /= pivot;
// c = c / a
array_1[c_idx] /= pivot;
// d = d / a
array_1[d_idx] /= pivot;
// S = S / a
array_2[S_idx] /= pivot;
}
int e_idx = array_1_entry + VLEN * 4 + j;
int f_idx = array_1_entry + VLEN * 5 + j;
int g_idx = array_1_entry + VLEN * 6 + j;
int k_idx = array_1_entry + VLEN * 7 + j;
int T_idx = array_2_entry + VLEN * 1 + j;
pivot = array_1[e_idx];
// f = f - (e * b)
array_1[f_idx] -= array_1[b_idx]
* pivot;
// g = g - (e * c)
array_1[g_idx] -= array_1[c_idx]
* pivot;
// k = k - (e * d)
array_1[k_idx] -= array_1[d_idx]
* pivot;
// T = T - (e * S)
array_2[T_idx] -= array_2[S_idx]
* pivot;
}
}
}
}
Для этого конкретного случая c G CC использует 16B-векторы вместо 32B-векторов для автоматической c векторизации. Довольно легко увидеть, что поток управления зависит от условия, которое может быть проверено из внутреннего l oop, но G CC не выполняет никакого l oop -переключения.
Отключение l oop может быть сделано вручную, но, пожалуйста, обратите внимание, что это минимальный случай проблемы, реальное l oop имеет сотни строк, и выполнение ручного переключения l oop приведет к большому количеству кода избыточность. Я пытаюсь найти способ заставить G CC создавать разные циклы для разных условий, которые можно проверить из внутреннего l oop.
В настоящее время я использую G CC 9.2 с следующие флаги: -Ofast -march=native -std=c11 -fopenmp -ftree-vectorize -ffast-math -mavx -mno-avx256-split-unaligned-load -mno-avx256-split-unaligned-store -fopt-info-vec-optimized