SSE XMM точка объяснение продукта - PullRequest
0 голосов
/ 29 мая 2018

У меня проблемы с пониманием определенного фрагмента кода в сборке.Задача состоит в том, чтобы найти скалярное произведение двух векторов, используя арифметику SSE и регистры XMM.Подход состоит в том, чтобы считывать векторы 4 с плавающей точкой за раз (то есть один регистр xmm будет содержать четыре за одну итерацию).Конечным результатом этого является регистр xmm, в котором каждый байт содержит сумму произведений (x1 * y1 + ...) заданных векторов.

То, что я не получаю, это часть, которая следует после.Все, что нужно для суммирования этих «конечных» байтов в целом, в основном составляет 4 байта, составляющих окончательный регистр.Я пытался найти что-то по этому поводу, но безуспешно.То, что мне дают, находится за пределами моего понимания, я даже пытался написать каждое вычисление на бумаге, ничего не имело особого смысла.В выделенной части фактическая сумма вычисляется и сохраняется в младшем байте xmm0.Любое понимание этого приветствуется.

.intel_syntax noprefix

.data
two: .int 2

.text
.global dot_product

############################################################################
##
## Function:
##
## void dot_product(float *x, float *y, int n, float *r);
##
## calculates the dot product of x and y (n lengths) and stores the result 
## in r
##
## -- float * x -- rdi -- 
## -- float * y -- rsi -- 
## -- int n -- rdx -- 
## -- float * r -- rcx -- 
##
############################################################################
dot_product:

        enter   0, 0


        mov r8, rcx
        mov r9, rdx


        mov     rax, 1
        cpuid
        test    rdx, 0x2000000
        jz not_supported


        mov     rdx, rsp
        and     rsp, 0xfffffffffffffff0
        sub     rsp, 512
        fxsave  [rsp]


        mov rcx, r9

    xorps xmm0, xmm0

next_four:

        cmp     rcx, 4
        jb next_one



        movups  xmm1, [rsi]
        movups  xmm2, [rdi]
    mulps xmm1, xmm2
    addps xmm0, xmm1



        add     rsi, 16
    add     rdi, 16  
        sub     rcx, 4
        jmp next_four

next_one:

        jrcxz finish




    movss  xmm1, [rsi]
        movss  xmm2, [rdi]
    mulss xmm1, xmm2
    addss xmm0, xmm1


        add     rsi, 4
    add     rdi, 4
        dec     rcx

        jmp next_one

finish: 

    #**summing the 4 bytes giving the actual dot product**
        movhlps xmm1, xmm0
        addps   xmm0, xmm1
        movaps  xmm1, xmm0
        shufps  xmm1, xmm1, 0b01010101
        addss   xmm0, xmm1


    movss   [r8], xmm0



        fxrstor [rsp]
        mov     rsp, rdx

done:

    leave
        ret

not_supported:


        mov rax, 1
        mov rbx, 1
        int 0x80

1 Ответ

0 голосов
/ 29 мая 2018

Этот окончательный код добавляет 4 упакованных числа с плавающей точкой в ​​xmm0, используя только обычные инструкции addps / addss.Сначала он копирует 2 самых верхних упакованных числа с плавающей точкой в ​​нижние числа с плавающей точкой xmm1, поэтому xmm0 + xmm1 может выполнить два добавления с одной инструкцией.2 высоких поплавка "безразличны".Повторите, используя shufps, чтобы скопировать самый высокий из оставшихся поплавков в самую низкую позицию.Думайте о непосредственном «селекторе» shufps как о индексе массива для каждого слова назначения.Единственное, что имеет значение - это младшие два бита, равные индексу 1, который перемещается на 1-> 0.Все остальные просто заполнители.Затем все, что нужно, - это одно добавление.

xmm0: D | C | B | A
 +
xmm1: X | X | D | C      (movhlps xmm0)
-------------------
 =    X | X | B+D | A+C


xmm0: X | X | B+D | A+C
 +
xmm1: X | X | X   | B+D   (shufps xmm0)
-----------------------
 =    X | X | X   | A + B + C + D

Здесь X означает «все равно».В конце сумма находится в самой низкой позиции, которую нужно извлечь с помощью инструкций сложения movss.

2, оставаясь в регистрах XMM.В противном случае вам нужно 3 с более явными ходами.

Дополнительные детали shufps:

Разделите значение 0b01010101 на 4 двоичных индекса: 01 |01 |01 |01, который в десятичном виде равен 1 |1 |1 |1. Каждый индекс выбирает источник (на словах) из источника.Это становится более сложным для более высоких 2 слов, как описано в документации, но нас это не волнует.Результатом является копирование word1 как в word0, так и в word1, поскольку оба нижних селектора равны 1.

EDIT: HADDPS - еще одна возможная реализация, добавляющая соседей.Две последовательности HADDPS позаботятся об окончательной сумме.Единственный способ узнать, что быстрее, - это выполнить эталонный тест на целевом процессоре, но этот последний фрагмент не должен иметь большого значения для общей скорости работы функции.

...