Каковы преимущества использования vaddss вместо addss в скалярном добавлении матрицы?
Я реализовал скалярное матричное сложение ядра.
#include <stdio.h>
#include <time.h>
//#include <x86intrin.h>
//loops and iterations:
#define N 128
#define M N
#define NUM_LOOP 1000000
float __attribute__(( aligned(32))) A[N][M],
__attribute__(( aligned(32))) B[N][M],
__attribute__(( aligned(32))) C[N][M];
int main()
{
int w=0, i, j;
struct timespec tStart, tEnd;//used to record the processiing time
double tTotal , tBest=10000;//minimum of toltal time will asign to the best time
do{
clock_gettime(CLOCK_MONOTONIC,&tStart);
for( i=0;i<N;i++){
for(j=0;j<M;j++){
C[i][j]= A[i][j] + B[i][j];
}
}
clock_gettime(CLOCK_MONOTONIC,&tEnd);
tTotal = (tEnd.tv_sec - tStart.tv_sec);
tTotal += (tEnd.tv_nsec - tStart.tv_nsec) / 1000000000.0;
if(tTotal<tBest)
tBest=tTotal;
} while(w++ < NUM_LOOP);
printf(" The best time: %lf sec in %d repetition for %dX%d matrix\n",tBest,w, N, M);
return 0;
}
В этом случае я скомпилировал программу с другим флагом компилятора, и результат сборки внутреннего цикла выглядит следующим образом:
gcc -O2 msse4.2
: Лучшее время: 0,000024 с при 406490 повторениях для матрицы 128X128
movss xmm1, DWORD PTR A[rcx+rax]
addss xmm1, DWORD PTR B[rcx+rax]
movss DWORD PTR C[rcx+rax], xmm1
gcc -O2 -mavx
: Лучшее время: 0,000009 с при повторении 1000001 для матрицы 128X128
vmovss xmm1, DWORD PTR A[rcx+rax]
vaddss xmm1, xmm1, DWORD PTR B[rcx+rax]
vmovss DWORD PTR C[rcx+rax], xmm1
Версия AVX gcc -O2 -mavx
:
__m256 vec256;
for(i=0;i<N;i++){
for(j=0;j<M;j+=8){
vec256 = _mm256_add_ps( _mm256_load_ps(&A[i+1][j]) , _mm256_load_ps(&B[i+1][j]));
_mm256_store_ps(&C[i+1][j], vec256);
}
}
Версия SSE gcc -O2 -sse4.2
::
__m128 vec128;
for(i=0;i<N;i++){
for(j=0;j<M;j+=4){
vec128= _mm_add_ps( _mm_load_ps(&A[i][j]) , _mm_load_ps(&B[i][j]));
_mm_store_ps(&C[i][j], vec128);
}
}
В скалярной программе ускорение -mavx
над msse4.2
составляет 2,7х. Я знаю avx
улучшил ISA эффективно, и это может быть из-за этих улучшений. Но когда я реализовал программу по сути для обоих AVX
а также SSE
ускорение в 3 раза. Вопрос в том, что скаляр AVX в 2,7 раза быстрее, чем в SSE, когда я его векторизовал, скорость в 3 раза (размер матрицы 128х128 для этого вопроса). Имеет ли это смысл При использовании AVX и SSE в скалярном режиме вы получаете ускорение в 2,7 раза. но векторизованный метод должен быть лучше, потому что я обрабатываю восемь элементов в AVX по сравнению с четырьмя элементами в SSE. Все программы имеют менее 4,5% кеша, так как perf stat
сообщили.
с помощью gcc -O2
, linux mint
, skylake
ОБНОВЛЕНИЕ: Вкратце, Scalar-AVX в 2,7 раза быстрее, чем Scalar-SSE, но AVX-256 только в 3 раза быстрее, чем SSE-128, пока он векторизован. Я думаю, что это может быть из-за конвейеризации. в скаляре у меня 3 vec-ALU
это не может быть использовано в векторизованном режиме. Я мог бы сравнить яблоки с апельсинами, а не яблоки с яблоками, и в этом может быть причина, по которой я не могу понять причину.
1 ответ
Проблема, которую вы наблюдаете, объясняется здесь. В системах Skylake, если верхняя половина регистра AVX загрязнена, существует ложная зависимость для операций SSE, не закодированных в vex, в верхней половине регистра AVX. В вашем случае кажется, что в вашей версии glibc 2.23 есть ошибка. В моей системе Skylake с Ubuntu 16.10 и glibc 2.24 у меня нет проблем. Ты можешь использовать
__asm__ __volatile__ ( "vzeroupper" : : : );
убрать верхнюю половину регистра AVX. Я не думаю, что вы можете использовать внутреннюю, такую как _mm256_zeroupper
чтобы исправить это, потому что GCC скажет, что это код SSE и не распознает внутреннее. Варианты -mvzeroupper
не будет работать, потому что GCC снова думает, что это код SSE и не будет излучать vzeroupper
инструкция.
Кстати, это вина Microsoft, что это проблема с оборудованием.
Обновить:
Другие люди, видимо, сталкиваются с этой проблемой на Skylake. Это наблюдалось после printf
, memset
, а также clock_gettime
,
Если ваша цель - сравнить 128-битные операции с 256-битными операциями, можно рассмотреть возможность использования -mprefer-avx128 -mavx
(что особенно полезно на AMD). Но тогда вы сравниваете AVX256 с AVX128, а не AVX256 с SSE. AVX128 и SSE используют 128-битные операции, но их реализации различны. Если вы проводите тестирование, вы должны указать, какой из них вы использовали.