Нужна некоторая конструктивная критика на моей попытке SSE/блока

Я работаю над преобразованием небольшого количества кода к 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

6
задан Joachim Sauer 5 May 2011 в 09:41
поделиться

1 ответ

Обычный способ сделать такую ​​векторизацию - это повернуть проблему «на бок». Вместо вычисления одного значения 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. С другой стороны, если бы вы могли делать четыре за раз, было бы намного быстрее.

9
ответ дан 10 December 2019 в 02:43
поделиться
Другие вопросы по тегам:

Похожие вопросы: