SSE42 & amp; STTNI — PcmpEstrM в два раза медленнее, чем PcmpIstrM, это правда?

Я экспериментирую с инструкциями SSE42 и STTNI и получил странный результат — PcmpEstrM (работает с явно заданными длинами строк) в два раза медленнее, чем PcmpIstrM (неявные строки длины).

  • На моем i7 3610QM разница в том, 2366,2 мс против 1202,3 мс — 97%.
  • На i5 3470 разница не так уж велика, но все же значительна = 3206,2 мс против 2623,2 мс — 22%.

Оба являются «Ivy Bridge» — странно, что у них такая разная «разница» (по крайней мере, я не вижу никаких технических различий в их спецификациях — http://www.cpu-world.com/Compare_CPUs/Intel_AW8063801013511,Intel_CM8063701093302/).

Справочное руководство по оптимизации архитектур Intel 64 и IA-32 упоминает одинаковую пропускную способность = 11 и задержку = 3 для PcmpEstrM и PcmpIstrM. Поэтому я ожидаю одинаковую производительность для обоих.

Q: Разница в том, что я практически спроектирован / ожидается, или я использую эти инструкции неправильно?

Ниже приведен сценарий моего фиктивного теста (VS 2012). Логика довольно проста — отсканируйте 16 МБ текста, чтобы найти соответствующий символ. Поскольку ни один из стога сена и игольной нити не содержит нулевых терминаторов — я ожидаю, что E и I будут иметь одинаковую производительность.

PS: я пытался опубликовать этот вопрос на форум разработчиков Intel, но они идентифицируют это как спам 🙁

#include "stdafx.h"#include <windows.h>
#define BEGIN_TIMER(NAME)                       \
{                                           \
LARGE_INTEGER   __freq;                 \
LARGE_INTEGER   __t0;                   \
LARGE_INTEGER   __t1;                   \
double          __tms;                  \
const char*     __tname = NAME;         \
char            __tbuf[0xff];           \
\
QueryPerformanceFrequency(&__freq);     \
QueryPerformanceCounter(&__t0);
#define END_TIMER()                             \
QueryPerformanceCounter(&__t1);         \
__tms = (__t1.QuadPart - __t0.QuadPart) * 1000.0 / __freq.QuadPart; \
sprintf_s(__tbuf, sizeof(__tbuf), "%-32s = %6.1f ms\n", __tname, __tms ); \
OutputDebugStringA(__tbuf);             \
printf(__tbuf);                         \
}
// 4.1.3 Aggregation Operation
#define SSE42_AGGOP_BITBASE         2
#define SSE42_AGGOP_EQUAL_ANY       (00b << SSE42_AGGOP_BITBASE)
#define SSE42_AGGOP_RANGES          (01b << SSE42_AGGOP_BITBASE)
#define SSE42_AGGOP_EQUAL_EACH      (10b << SSE42_AGGOP_BITBASE)
#define SSE42_AGGOP_EQUAL_ORDERED   (11b << SSE42_AGGOP_BITBASE)
int _tmain(int argc, _TCHAR* argv[])
{
int cIterations = 1000000;
int cCycles = 1000;
int cchData = 16 * cIterations;
char* testdata = new char[cchData + 16];

memset(testdata, '*', cchData);
testdata[cchData - 1] = '+';
testdata[cchData] = '\0';
BEGIN_TIMER("PcmpIstrI") {
for( int i = 0; i < cCycles; i++ ) {
__asm {
push        ecx
push        edx
push        ebx
mov         edi, testdata
mov         ebx, cIterations
mov         al, '+'
mov         ah, al
movd        xmm1, eax               // fill low word with pattern
pshuflw     xmm1, xmm1, 0           // fill low dqword with pattern
movlhps     xmm1, xmm1              // ... and copy it hi dqword
loop_pcmpistri:
PcmpIstrM   xmm1, [edi], SSE42_AGGOP_EQUAL_EACH
add         edi, 16
sub         ebx, 1
jnz         loop_pcmpistri
pop         ebx
pop         edx
pop         ecx
}
}
} END_TIMER();
BEGIN_TIMER("PcmpEstrI") {
for( int i = 0; i < cCycles; i++ ) {
__asm {
push        ecx
push        edx
push        ebx
mov         edi, testdata
mov         ebx, cIterations
mov         al, '+'
mov         ah, al
movd        xmm1, eax               // fill low word with pattern
pshuflw     xmm1, xmm1, 0           // fill low dqword with pattern
movlhps     xmm1, xmm1              // ... and copy it hi dqword
mov         eax, 15
mov         edx, 15
loop_pcmpestri:
PcmpEstrM   xmm1, [edi], SSE42_AGGOP_EQUAL_EACH
add         edi, 16
sub         ebx, 1
jnz         loop_pcmpestri
pop         ebx
pop         edx
pop         ecx
}
}
} END_TIMER();
return 0;
}

7

Решение

Согласно инструкции таблицы Агнер туман, pcmpestrm занимает 8 мкс, тогда как pcmpistrm занимает 3 µops на большинстве архитектур. Это должно объяснить разницу в производительности, которую вы наблюдаете. Попробуйте переписать свой код, чтобы вы могли использовать pcmpistrm вместо pcmpestrm если возможно.

2

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]