2017-02-05 28 views
2

Я применил program, используя SSE2, чтобы сравнить инструкцию vpsadbw и psadbw от AVX2 и SSE2 соответственно. Следующий код является программа SSE2:В чем причина этого странного поведения печальной инструкции?

#define MAX1 4096 
#define MAX2 MAX1 
#define MAX3 MAX1 

#define NUM_LOOP 1000000000 

double pTime = 0, mTime = 5; 

//global data for sequentila matrix operations 
unsigned char a_char[MAX1][MAX2] __attribute__((aligned(16))); 
unsigned char b_char[MAX2][MAX3] __attribute__((aligned(16))); 
unsigned char c_char[MAX1][MAX3] __attribute__((aligned(16))); 
unsigned short int temp[8]; 


int main() 
{ 
    int i, j, w=0, sad=0; 
    struct timespec tStart, tEnd; 
    double tTotal , tBest=10000; 
    __m128i vec1, vec2, vecT, sad_total; 
    sad_total= _mm_setzero_si128(); 

    do{ 
     clock_gettime(CLOCK_MONOTONIC,&tStart); 

     for(i=0; i<MAX1; i++){ 
      for(j=0; j<MAX2; j+=16){ 

       vec1 = _mm_load_si128((__m128i *)&a_char[i][j]); 
       vec2 = _mm_load_si128((__m128i *)&b_char[i][j]); 
       vecT = _mm_sad_epu8(vec1 , vec2); 
       sad_total = _mm_add_epi64(vecT, sad_total); 

       } 
      } 
     _mm_store_si128((__m128i *)&temp[0], sad_total); 
     sad=temp[0]+temp[2]+temp[4]+temp[6];  

     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; 
     pTime += tTotal; 

    } while(w++ < NUM_LOOP && pTime < mTime); 
    printf(" The best time: %lf sec in %d repetition for %dX result is %d matrix\n",tBest,w, MAX1, sad); 

    return 0; 
} 

Я использую gcc, skylake, Linux mint Когда я генерировать код сборки внутренний цикл содержит некоторые нежелательные операции перемещения следующим за SSE2:

.L26: 
    vmovdqa xmm1, XMMWORD PTR a_char[rcx+rax] 
    vpsadbw xmm1, xmm1, XMMWORD PTR b_char[rcx+rax] 
    add rax, 16 
    vpaddq xmm3, xmm1, XMMWORD PTR [rsp] 
    cmp rax, 4096 
    vmovaps XMMWORD PTR [rsp], xmm3 
    jne .L26 

С AVX2 генерирует этот ассемблерный код:

.L26: 
    vmovdqa ymm1, YMMWORD PTR a_char[rcx+rax] 
    vpsadbw ymm1, ymm1, YMMWORD PTR b_char[rcx+rax] 
    add rax, 32 
    vpaddq ymm2, ymm2, ymm1 
    cmp rax, 4096 
    jne .L26 

Я не знаю, почему эти 2 движущиеся объекты который значительно нарушил работу.

+0

Хорошо, я не знал, что это не доступно. – Martin

ответ

3

Причина заключается в следующем:

_mm_store_si128((__m128i *)&temp[0], sad_total); 

Clang не возражает и делает хороший код независимо, но GCC не нравится (неудачные эвристики возможно?)

С, который заменил на то, что не запускает «это должно быть в стеке все время» -heuristic, GCC делает более хороший код, например: (не проверено)

__m128i sad_total = _mm_setzero_si128(); 
    for(i = 0; i < MAX1; i++) { 
     for(j = 0; j < MAX2; j += 16) { 
      __m128i vec1 = _mm_load_si128((__m128i *)&a_char[i][j]); 
      __m128i vec2 = _mm_load_si128((__m128i *)&b_char[i][j]); 
      __m128i vecT = _mm_sad_epu8(vec1 , vec2); 
      sad_total = _mm_add_epi64(sad_total, vecT); 
     } 
    } 
    __m128i hsum = _mm_add_epi64(sad_total, _mm_bsrli_si128(sad_total, 8)); 
    sad = _mm_cvtsi128_si32(hsum); 

внутренний цикл выглядит теперь как

.L2: 
    vmovdqa xmm1, XMMWORD PTR a_char[rdx+rax] 
    vpsadbw xmm1, xmm1, XMMWORD PTR b_char[rdx+rax] 
    add  rax, 16 
    vpaddq xmm2, xmm1, xmm2 
    cmp  rax, 4096 
    jne  .L2 
0

Вы просто обходите компилятор и говорите ему, чтобы использовать movdqa через _mm_load_si128. Это делает именно то, что вы говорите. В чем проблема? Я также заметил, что вы выравниваете границу 16-го байта, не стесняйтесь исправлять меня, если я ошибаюсь (я не уверен, как атрибут реализован на вашем компиляторе), но вы можете получить дополнение в качестве результата, чтобы каждый элемент будут выровнены по границе 16 байт; если это повлияет на влияние вашего разворачивания. Если нет, то не стесняйтесь меня исправлять.

+0

Все это должно быть загружено в любом случае, поэтому я предполагаю, что проблема заключается в том, что аккумулятор стекается в стек – harold

+0

Атрибут выравнивания _only_ выравнивает _first_ байт (т. Е. Без заполнения). То есть, компоновщик/загрузчик гарантирует, что переменная имеет адрес, так что '(((unsigned long) & x [0] [0])% 16) == 0'. Поддержание такого выравнивания [при индексировании в массив] зависит от использования (т. Е. Программного кода). Чтобы корректно работать, заданный 'unsigned char a_char [MAX1] [MAX2]', 'MAX2' должен быть кратным 16 и при индексировании с помощью' a_char [i] [j] ',' for (j = 0; j

+0

Cheers Craig. Меня беспокоит, что если вы используете это для каждого члена структуры (то есть __attribute __ (aligned (16))), то do sizeof() в структуре увеличивает размер сообщенного размера с изменениями параметра выравнивания (не может проверить это сейчас)? Не уверен, как это влияет на статически объявленные массивы, хотя, я думаю, это, когда ваша точка приходит. Не могу проверить это, но я использовал «эквивалент», используя VS, и было добавление. Просто подумал, что это может стоить подняться как предостережение. – cdcdcd