2010-12-09 3 views
-1

Следующий цикл выполняется сотни раз.
elma and elmc are both unsigned long (64-bit) arrays, so is res1 and res2.Код SIMD против Скалярного кода

unsigned long simdstore[2]; 
__m128i *p, simda, simdb, simdc; 
p = (__m128i *) simdstore; 

for (i = 0; i < _polylen; i++) 
{ 

    u1 = (elma[i] >> l) & 15; 
    u2 = (elmc[i] >> l) & 15; 
    for (k = 0; k < 20; k++) 
    {  

    1. //res1[i + k] ^= _mulpre1[u1][k]; 
    2. //res2[i + k] ^= _mulpre2[u2][k];    
    3.  _mm_prefetch ((const void *) &_mulpre2[u2][k], _MM_HINT_T0); 
    4.  _mm_prefetch ((const void *) &_mulpre1[u1][k], _MM_HINT_T0); 
    5.  simda = _mm_set_epi64x (_mulpre2[u2][k], _mulpre1[u1][k]); 
    6.  _mm_prefetch ((const void *) &res2[i + k], _MM_HINT_T0); 
    7.  _mm_prefetch ((const void *) &res1[i + k], _MM_HINT_T0); 
    8.  simdb = _mm_set_epi64x (res2[i + k], res1[i + k]); 
    9.  simdc = _mm_xor_si128 (simda, simdb); 
    10.  _mm_store_si128 (p, simdc); 
    11.  res1[i + k] = simdstore[0]; 
    12.  res2[i + k] = simdstore[1];      
    }  
} 

Внутри цикла for скалярная версия кода (прокомментирована) выполняется в два раза быстрее, чем код simd. С описанием cachegrind (Инструкции) приведенных выше строк упоминается ниже.

Строка 1: 668460000 2 2
Строка 2: 668460000 1 1
Строка 3: 89985000 1 1
Строка 4: 89985000 1 1
Строка 5: 617040000 2 2
Строка 6: 44992500 0 0
Строка 7: 44992500 0 0
Строка 8: 539910000 1 1
Строка 9: 128550000 0 0
Строка 10:. , ,
Строка 11: 205680000 0 0
Строка 12: 205.680.000 0 0

Из приведенного выше рисунка, оказывается, что заметили (скалярный код) требует значительно меньшего количества инструкций SIMD, чем код.

Как этот код можно сделать быстрее?

+0

Это дубликат вашего собственного вопроса.Вернитесь туда и поймите, почему этот ответ (а не ответ, который вы отметили как правильный) решает проблему: http://stackoverflow.com/questions/4394930/simd-code-runs-slower-than-scalar-code/4395337# 4395337 – 2010-12-09 12:27:29

ответ

3

Извлеките встроенные функции _mm_prefetch - они ничего не добиваются в этом контексте и могут даже повредить производительность. Предварительная выборка выигрывает только в том случае, если (а) у вас есть пропускная способность, и (б) вы можете выпустить подсказку предварительной выборки за несколько сотен тактовых циклов до того, когда данные действительно понадобятся. Я думаю, что ни а), ни (б) не верны в вашем случае.

1

Ваша проблема Peformance заключается в следующем:

_mm_set_epi64x (_mulpre2 [u2] [к], _mulpre1 [u1] [к]);

Класс встроенных функций mm_set (a, b, c, d) очень медленный. Быстро выполняются только встроенные параметры набора параметров (aka broadcast).

Я посмотрел, что они делают в ассемблере.

Они в основном создают массив в стеке, перемещают ваши два целых числа из многомерных массивов, в которых они в настоящее время находятся, в массив стека, используя обычные перемещения памяти (mov DWORD). Затем из массива стека, используя перемещение памяти XMM (mov XMWORD).

Скалярная версия переходит непосредственно из памяти в регистры. БЫСТРЕЕ!

Вы видите, что накладные расходы связаны с тем, что регистр XMM может передаваться только по 128 бит за раз, поэтому ваша программа сначала заказывает 128 бит в другой области памяти перед их загрузкой.

Если есть способ переместить 64-битные значения непосредственно в или из обычного регистра в регистр XMM, я все еще ищу его.

Чтобы получить ускорение скорости от использования регистров SSE/XMM, ваши данные, вероятно, должны быть в порядке в памяти. Загрузка данных о заказе в регистр XMM стоит того, если вы можете выполнять несколько операций XMM за загрузку заказа. Здесь вы делаете одну операцию XOR.