Я пытаюсь представить себе ускорение за счет включения AVX2 и AVX512.
#include <stdio.h>
#include <stdlib.h>
#include <immintrin.h>
#include <omp.h>
#include <time.h>
int main()
{
long i, N = 160000000;
int * A = (int *)aligned_alloc(sizeof(__m256), sizeof(int) * N);
int * B = (int *)aligned_alloc(sizeof(__m256), sizeof(int) * N);
int * C = (int *)aligned_alloc(sizeof(__m256), sizeof(int) * N);
int * E = (int *)aligned_alloc(sizeof(__m512), sizeof(int) * N);
int * F = (int *)aligned_alloc(sizeof(__m512), sizeof(int) * N);
int * G = (int *)aligned_alloc(sizeof(__m512), sizeof(int) * N);
srand(time(0));
for(i=0;i<N;i++)
{
A[i] = rand();
B[i] = rand();
E[i] = rand();
F[i] = rand();
}
double time = omp_get_wtime();
for(i=0;i<N;i++)
{
C[i] = A[i] + B[i];
}
time = omp_get_wtime() - time;
printf("General Time taken %lf\n", time);
__m256i A_256_VEC, B_256_VEC, C_256_VEC;
time = omp_get_wtime();
for(i=0;i<N;i+=8)
{
A_256_VEC = _mm256_load_si256((__m256i *)&A[i]);
B_256_VEC = _mm256_load_si256((__m256i *)&B[i]);
C_256_VEC = _mm256_add_epi32(A_256_VEC, B_256_VEC);
_mm256_store_si256((__m256i *)&C[i],C_256_VEC);
}
time = omp_get_wtime() - time;
printf("AVX2 Time taken %lf\n", time);
free(A);
free(B);
free(C);
__m512i A_512_VEC, B_512_VEC, C_512_VEC;
time = omp_get_wtime();
for(i=0;i<N;i+=16)
{
A_512_VEC = _mm512_load_si512((__m512i *)&E[i]);
B_512_VEC = _mm512_load_si512((__m512i *)&F[i]);
C_512_VEC = _mm512_add_epi32(A_512_VEC, B_512_VEC);
_mm512_store_si512((__m512i *)&G[i],C_512_VEC);
}
time = omp_get_wtime() - time;
printf("AVX512 Time taken %lf\n", time);
for(i=0;i<N;i++)
{
if(G[i] != E[i] + F[i])
{
printf("Not Matched !!!\n");
break;
}
}
free(E);
free(F);
free(G);
return 1;
}
Итак, код распространяется в три этапа. Присутствуют три массива. Это просто добавление массива. Сначала мы выполняем это с помощью общего цикла, затем с помощью AVX2, а затем AVX 512. Я использую процессор Intel Xeon 6130.
Код компилируется с помощью команды,
gcc -o test.o test.c -mavx512f -fopenmp -mavx2
Результат:
General Time taken 0.532550
AVX2 Time taken 0.175549
AVX512 Time taken 0.264475
Теперь ускорение видно в случае общих циклов и встроенных реализаций. Но время увеличено с AVX2 до AVX512, чего теоретически быть не должно.
Я проверил отдельные операции загрузки, добавления, хранения. Работа магазина AVX512 занимает максимальное время.
Просто ради проверки, удаляю ли я операцию сохранения из обоих сегментов кода, результирующие тайминги будут такими:
General Time taken 0.530248
AVX2 Time taken 0.115234
AVX512 Time taken 0.107062
Может ли кто-нибудь пролить свет на такое поведение или это ожидается?
********* ОБНОВЛЕНИЕ 1 *********
После компиляции с -O3 -march = native extension новые тайминги:
General Time taken 0.014887
AVX2 Time taken 0.008072
AVX512 Time taken 0.014630
Это все инструкции по загрузке, добавлению и сохранению.
********* ОБНОВЛЕНИЕ 2 *********
Тест 1:
Общий цикл был изменен следующим образом:
for(i=0;i<N;i++)
{
//C[i] = A[i] + B[i];
//G[i] = E[i] + F[i];
}
Результат:
General Time taken 0.000003
AVX2 Time taken 0.014877
AVX512 Time taken 0.014334
Поэтому в обоих случаях происходит сбой страницы.
Тест 2:
Общий цикл был изменен следующим образом:
for(i=0;i<N;i++)
{
C[i] = A[i] + B[i];
G[i] = E[i] + F[i];
}
Итак, кеширование выполняется в обоих случаях.
Выход,
General Time taken 0.029703
AVX2 Time taken 0.008500
AVX512 Time taken 0.008560
Тест 3:
Во всех сценариях добавляется фиктивный внешний цикл, а размер N уменьшается до 160000.
for(j=0;j<N;j++)
{
for(i=0;i<N;i+= /* 1 or 8 or 16 */)
{
// Code
}
}
Теперь на выходе
General Time taken 6.969532
AVX2 Time taken 0.871133
AVX512 Time taken 0.447317
-O3 -march=native
для оптимизации и настройки вашего процессора. (И включите все поддерживаемые им расширения ISA.) Хотя оставление AVX512VL отключенным может помешать GCC быть глупым и использовать его без надобности. Возможно, GCC делает что-то еще более неэффективное в цикле AVX512, чем в цикле AVX2. - person Peter Cordes   schedule 04.02.2020A_256_VEC = _mm256_load_si256((__m256i *)&A[i]);
будет компилироваться в блок, который загружает адрес массива иi
из стека, использует его для загрузкиvmovdqa
, а затем сохраняет результирующий вектор в стек (в объектеA_256_VEC
). Или, возможно, с дополнительной копией из-за накладных расходов из-за отказа от оптимизации встроенных функций, которые встраиваются. Но да, после исправления команды компиляции работа может быть полностью оптимизирована, если удалить хранилища. - person Peter Cordes   schedule 04.02.2020-O3
. Или, может быть, вы использовали-O2
или-O3 -fno-tree-vectorize
, чтобы обмануть компилятор и заставить его делать медленный скалярный код, даже не SSE2. - person Peter Cordes   schedule 05.02.2020