SSE / NEON оптимизация поиска таблиц

У меня есть следующий код поиска и интерполяции для оптимизации. (плавающий стол размером 128)
Он будет использоваться с компилятором Intel для Windows, GCC для OSX и GCC с неоновым OSX.

for(unsigned int i = 0 ; i < 4 ; i++)
{
const int iIdx = (int)m_fIndex[i];
const float frac = m_fIndex - iIdx;
m_fResult[i] = sftable[iIdx].val + sftable[iIdx].val2 * frac;
}

я все проверил с помощью sse / neon. (макросы конвертируются в инструкции sse / neon)

VEC_INT iIdx = VEC_FLOAT2INT(m_fIndex);
VEC_FLOAT frac = VEC_SUB(m_fIndex ,VEC_INT2FLOAT(iIdx);
m_fResult[0] = sftable[iIdx[0]].val2;
m_fResult[1] = sftable[iIdx[1]].val2;
m_fResult[2] = sftable[iIdx[2]].val2;
m_fResult[3] = sftable[iIdx[3]].val2;
m_fResult=VEC_MUL( m_fResult,frac);
frac[0] = sftable[iIdx[0]].val1;
frac[1] = sftable[iIdx[1]].val1;
frac[2] = sftable[iIdx[2]].val1;
frac[3] = sftable[iIdx[3]].val1;
m_fResult=VEC_ADD( m_fResult,frac);

я думаю, что доступ к таблице и перемещение в выровненную память — реальное узкое место здесь.
Я не очень разбираюсь в ассемблере, но есть много unpcklps и mov:

10026751  mov         eax,dword ptr [esp+4270h]
10026758  movaps      xmm3,xmmword ptr [eax+16640h]
1002675F  cvttps2dq   xmm5,xmm3
10026763  cvtdq2ps    xmm4,xmm5
10026766  movd        edx,xmm5
1002676A  movdqa      xmm6,xmm5
1002676E  movdqa      xmm1,xmm5
10026772  psrldq      xmm6,4
10026777  movdqa      xmm2,xmm5
1002677B  movd        ebx,xmm6
1002677F  subps       xmm3,xmm4
10026782  psrldq      xmm1,8
10026787  movd        edi,xmm1
1002678B  psrldq      xmm2,0Ch
10026790  movdqa      xmmword ptr [esp+4F40h],xmm5
10026799  mov         ecx,dword ptr [eax+edx*8+10CF4h]
100267A0  movss       xmm0,dword ptr [eax+edx*8+10CF4h]
100267A9  mov         dword ptr [eax+166B0h],ecx
100267AF  movd        ecx,xmm2
100267B3  mov         esi,dword ptr [eax+ebx*8+10CF4h]
100267BA  movss       xmm4,dword ptr [eax+ebx*8+10CF4h]
100267C3  mov         dword ptr [eax+166B4h],esi
100267C9  mov         edx,dword ptr [eax+edi*8+10CF4h]
100267D0  movss       xmm7,dword ptr [eax+edi*8+10CF4h]
100267D9  mov         dword ptr [eax+166B8h],edx
100267DF  movss       xmm1,dword ptr [eax+ecx*8+10CF4h]
100267E8  unpcklps    xmm0,xmm7
100267EB  unpcklps    xmm4,xmm1
100267EE  unpcklps    xmm0,xmm4
100267F1  mulps       xmm0,xmm3
100267F4  movaps      xmmword ptr [eax+166B0h],xmm0
100267FB  mov         ebx,dword ptr [esp+4F40h]
10026802  mov         edi,dword ptr [esp+4F44h]
10026809  mov         ecx,dword ptr [esp+4F48h]
10026810  mov         esi,dword ptr [esp+4F4Ch]
10026817  movss       xmm2,dword ptr [eax+ebx*8+10CF0h]
10026820  movss       xmm5,dword ptr [eax+edi*8+10CF0h]
10026829  movss       xmm3,dword ptr [eax+ecx*8+10CF0h]
10026832  movss       xmm6,dword ptr [eax+esi*8+10CF0h]
1002683B  unpcklps    xmm2,xmm3
1002683E  unpcklps    xmm5,xmm6
10026841  unpcklps    xmm2,xmm5
10026844  mulps       xmm2,xmm0
10026847  movaps      xmmword ptr [eax+166B0h],xmm2

При профилировании не так много преимуществ с версией sse на win.

Есть ли у вас какие-либо предложения, как улучшить?
Ожидаются ли какие-либо побочные эффекты с неоном / gcc?

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

2

Решение

OSX? Тогда это не имеет ничего общего с НЕОН.

Кстати, NEON все равно не может справиться с такими большими LUT. (Я не знаю о SSE по этому вопросу)

Сначала проверьте, может ли SSE обрабатывать LUT такого размера, если да, я предлагаю использовать другой компилятор, так как GCC имеет тенденцию делать взломы из встроенных.

2

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

Это один из самых худших коденов компилятора, который я когда-либо видел (при условии, что оптимизатор включен). Стоит подать ошибку против GCC.

Главные проблемы:

  • загрузка val а также val2 для каждого поиска отдельно.
  • Получение индекса для val а также val2 в георадар отдельно.
  • Запись вектора индексов в стек и затем загрузка их в георадары.

Чтобы компиляторы могли генерировать лучший код (по одной загрузке для каждой строки таблицы), вам может потребоваться загрузить каждую строку таблицы, как если бы она была двойной, а затем привести ее к вектору двух чисел с плавающей запятой и перевернуть строки, чтобы получить однородность векторы. Как на NEON, так и на SSE, для этого потребуется только четыре загрузки и три или четыре распаковки (намного лучше, чем текущие восемь загрузок + шесть распаковок).

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

1

Одна из причин, по которой компилятор создает «прикольный» код (с большим количеством повторных загрузок), заключается в том, что для правильности он должен предполагать, что данные в sftable[] массивы может поменяться. Чтобы сделать сгенерированный код лучше, реструктурируйте его так:

VEC_INT iIdx = VEC_FLOAT2INT(m_fIndex);
VEC_FLOAT frac = VEC_SUB(m_fIndex ,VEC_INT2FLOAT(iIdx);
VEC_FLOAT fracnew;

// make it explicit that all you want is _four loads_
typeof(*sftable) tbl[4] = {
sftable[iIdx[0]], sftable[iIdx[1]], sftable[iIdx[2]], sftable[iIdx[3]]
};

m_fResult[0] = tbl[0].val2
m_fResult[1] = tbl[1].val2;
m_fResult[2] = tbl[2].val2;
m_fResult[3] = tbl[3].val2;
fracnew[0] = tbl[0].val1;
fracnew[1] = tbl[1].val1;
fracnew[2] = tbl[2].val1;
fracnew[3] = tbl[3].val1;

m_fResult=VEC_MUL( m_fResult,frac);
m_fResult=VEC_ADD( m_fResult,fracnew);
frac = fracnew;

Это может иметь смысл (из-за чередование макет того, что у вас есть в sftable[]) использовать встроенные функции, потому что оба векторных массива с плавающей точкой fResult а также frac вполне вероятно, загружаемые из tbl[] с одной инструкцией (распаковать привет / ло в SSE, распаковать в неон). «Основной» просмотр таблицы не может быть векторизован без помощи чего-то вроде AVX2 VGATHER инструкция, но это не должно быть больше, чем четыре нагрузки.

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