Я работаю над преобразованием небольшого количества кода к SSE, и в то время как у меня есть корректный вывод, это оказывается медленнее, чем стандартный код C++.
Бит кода, для которого я должен сделать это:
float ox = p2x - (px * c - py * s)*m;
float oy = p2y - (px * s - py * c)*m;
То, что я имею для кода SSE:
void assemblycalc(vector4 &p, vector4 &sc, float &m, vector4 &xy)
{
vector4 r;
__m128 scale = _mm_set1_ps(m);
__asm
{
mov eax, p //Load into CPU reg
mov ebx, sc
movups xmm0, [eax] //move vectors to SSE regs
movups xmm1, [ebx]
mulps xmm0, xmm1 //Multiply the Elements
movaps xmm2, xmm0 //make a copy of the array
shufps xmm2, xmm0, 0x1B //shuffle the array
subps xmm0, xmm2 //subtract the elements
mulps xmm0, scale //multiply the vector by the scale
mov ecx, xy //load the variable into cpu reg
movups xmm3, [ecx] //move the vector to the SSE regs
subps xmm3, xmm0 //subtract xmm3 - xmm0
movups [r], xmm3 //Save the retun vector, and use elements 0 and 3
}
}
Начиная с его очень трудного для чтения кода я объясню, что я сделал:
загруженный vector4, xmm0 _____ p = [пкс, py, пкс, py]
mult. vector4, xmm1 _ cs = [c, c, s, s]
__________________________ mult----------------------------
результат, _____________ xmm0 = [pxc, pyc, pxs, pys]
результат повторного использования, xmm0 = [pxc, pyc, pxs, pys]
переставьте результат, xmm2 = [pys, pxs, pyc, pxc]
_____________________ вычтите----------------------------
результат, xmm0 = [pxc-pys, pyc-pxs, pxs-pyc, pys-pxc]
результат повторного использования, xmm0 = [pxc-pys, pyc-pxs, pxs-pyc, pys-pxc]
загрузите m vector4, масштаб = [m, m, m, m]
__________________________ mult----------------------------
результат, xmm0 = [(pxc-pys) m, (pyc-px*s) m, (pxs-py*c) m, (pys-px*c) m]
xy vector4 загрузки, xmm3 = [p2x, p2x, p2y, p2y]
повторное использование, xmm0 = [(pxc-py*s) m, (pyc-px*s) m, (pxs-py*c) m, (pys-px*c) m]
_____________________ вычтите----------------------------
результат, xmm3 = [p2x-(pxc-py*s) m, p2x-(pyc-px*s) m, p2y-(pxs-py*c) m, p2y-(pys-px*c) *m]
затем вол = xmm3[0] и внук = xmm3[3], таким образом, я по существу не использую xmm3[1] или xmm3[4]
Я приношу извинения за трудность, читая это, но я надеюсь, что кто-то смог обеспечивать некоторое руководство для меня, когда стандартный код C++ работает в 0,001444 мс и выполнения кода SSE в 0,00198 мс.
Сообщите мне, существует ли что-нибудь, что я могу сделать для дальнейшего объяснения/чистившего этого немного. Причина я пытаюсь использовать SSE, состоит в том, потому что я выполняю это вычисление миллионы времен, и это - часть того, что замедляет мой текущий код.
Заранее спасибо за любую справку! Brett
Обычный способ сделать такую векторизацию - это повернуть проблему «на бок». Вместо вычисления одного значения ox
и oy
, вы вычисляете четыре значения ox
и четыре значения oy
одновременно. Это сводит к минимуму бесполезные вычисления и перемешивание.
Для этого необходимо объединить несколько значений x
, y
, p2x
и p2y
в смежные массивы (т. Е. у вас может быть массив из четырех значений x
, массив из четырех значений y
и т. д.). Тогда вы можете просто сделать:
movups %xmm0, [x]
movups %xmm1, [y]
movaps %xmm2, %xmm0
mulps %xmm0, [c] // cx
movaps %xmm3, %xmm1
mulps %xmm1, [s] // sy
mulps %xmm2, [s] // sx
mulps %xmm3, [c] // cy
subps %xmm0, %xmm1 // cx - sy
subps %xmm2, %xmm3 // sx - cy
mulps %xmm0, scale // (cx - sy)*m
mulps %xmm2, scale // (sx - cy)*m
movaps %xmm1, [p2x]
movaps %xmm3, [p2y]
subps %xmm1, %xmm0 // p2x - (cx - sy)*m
subps %xmm3, %xmm2 // p2y - (sx - cy)*m
movups [ox], %xmm1
movups [oy], %xmm3
Используя этот подход, мы вычисляем 4 результата одновременно в 18 инструкциях, по сравнению с одним результатом в 13 инструкциях при вашем подходе. Мы также не теряем результатов.
Это еще можно улучшить; так как вам все равно придется переупорядочивать структуры данных, чтобы использовать этот подход, вам следует выровнять массивы и использовать выровненные загрузки и сохранения вместо невыровненных. Вы должны загрузить c и s в регистры и использовать их для обработки многих векторов x и y, вместо того, чтобы перезагружать их для каждого вектора. Для достижения наилучшей производительности два или более вектора, требующие вычисления, должны чередоваться, чтобы убедиться, что у процессора достаточно работы для предотвращения остановок конвейера.
(Примечание: должно ли это быть cx + sy
вместо cx - sy
? Это даст вам стандартную матрицу вращения)
Править
Ваш комментарий о том, на каком оборудовании вы используете тайминги, в значительной степени проясняет все: «Pentium 4 HT, 2,79 ГГц».Это очень старая микроархитектура, в которой невыровненные ходы и тасовки выполняются довольно медленно; у вас недостаточно работы в конвейере, чтобы скрыть задержку арифметических операций, а механизм переупорядочения далеко не так умен, как в новых микроархитектурах.
Я ожидаю, что ваш векторный код окажется быстрее, чем скалярный код на i7 и, вероятно, также на Core2. С другой стороны, если бы вы могли делать четыре за раз, было бы намного быстрее.