Сравнение чисел с плавающей точкой в SIMD

Последнее обновление: 03.07.2023

Для сравнения чисел с плавающей точкой расширения SSE/AVX предоставляют инструкции (v)cmpps (сравнивает числа одинарной точности) и (v)cmppd (для чисел двойной точности):

cmpps xmmdest, xmmsrc/mem128, imm8
vcmpps xmmdest, xmmsrc1, xmmsrc2/mem128, imm8
vcmpps ymmdest, ymmsrc1, ymmsrc2/mem256, imm8

cmppd xmmdest, xmmsrc/mem128, imm8
vcmppd xmmdest, xmmsrc1, xmmsrc2/mem128, imm8
vcmppd ymmdest, ymmsrc1, ymmsrc2/mem256, imm8

Стоит отметить, что сравнения могут быть упорядоченными (ordered) и неупорядоченными (unordered). При неупорядоченном сравнении как минимум один из операндов представляет значение NaN. Упорядоченное сравнение, наоборот, - это такое сравнение, где ни один из операндов не является NaN.

Кроме того, сравнения могут быть сигнальные (signaling) и тихие (quiet). Сигнальные сравнения генерируют исключение недопустимой арифметической операции (IA), если результатом операцим является NaN. Тихие сравнения не вызывают исключения и отражают только состояние в регистре MXCSR.

Последний операнд инструкций - imm8 указывает на тип сравнения.

  • 00h: проверяет операнды на равенство, если они равны, устанавливает для всех битов дорожки 1, иначе устанавливает 0, (сравнение упорядоченное, тихое)

  • 01h: если первый операнд меньше второго, устанавливает 1 (сравнение упорядоченное, сигнальное)

  • 02h: если первый операнд меньше или равен второму, устанавливает 1 (сравнение упорядоченное, сигнальное)

  • 03h: устанавливает 1, если сравенение неупорядоченное (сравнение неупорядоченное, тихое)

  • 04h: если операнды не равны, устанавливает 1 (сравнение неупорядоченное, тихое)

  • 05h: если первый операнд НЕ меньше второго, устанавливает 1 (сравнение неупорядоченное, сигнальное)

  • 06h: если первый операнд НЕ меньше и НЕ равен второму, устанавливает 1 (сравнение неупорядоченное, сигнальное)

  • 07h: устанавливает 1, если сравенение упорядоченное (сравнение упорядоченное, тихое)

  • 08h: если операнды равны, устанавливает 1 (сравнение неупорядоченное, тихое)

  • 09h: если первый операнд НЕ больше и НЕ равен второму, устанавливает 1 (сравнение неупорядоченное, сигнальное)

  • 0Ah: если первый операнд НЕ больше второго, устанавливает 1 (сравнение неупорядоченное, сигнальное)

  • 0Bh: возвращает 0, если сравнение упорядоченное (сравнение упорядоченное, тихое)

  • 0Ch: если операнды не равны, устанавливает 1 (сравнение упорядоченное, тихое)

  • 0Dh: если первый операнд больше или равен второму, устанавливает 1 (сравнение упорядоченное, сигнальное)

  • 0Eh: если первый операнд больше второго, устанавливает 1 (сравнение упорядоченное, сигнальное)

  • 0Fh: устанавливает 1, если сравнение неупорядоченное (сравнение неупорядоченное, тихое)

  • 10h: если операнды равны, устанавливает 1 (сравнение упорядоченное, сигнальное)

  • 11h: если первый операнд меньше второго, устанавливает 1 (сравнение упорядоченное, тихое)

  • 12h: если первый операнд меньше или равен второму, устанавливает 1 (сравнение упорядоченное, тихое)

  • 13h: устанавливает 1, если сравнение неупорядоченное (сравнение неупорядоченное, сигнальное)

  • 14h: если операнды не равны, устанавливает 1 (сравнение неупорядоченное, сигнальное)

  • 15h: если первый операнд НЕ меньше второго, устанавливает 1 (сравнение неупорядоченное, тихое)

  • 16h: если первый операнд НЕ меньше и НЕ равен второму, устанавливает 1 (сравнение неупорядоченное, тихое)

  • 17h: устанавливает 1, если сравнение упорядоченное (сравнение упорядоченное, сигнальное)

  • 18h: если операнды равны, устанавливает 1 (сравнение неупорядоченное, сигнальное)

  • 19h: если первый операнд НЕ больше и НЕ равен второму, устанавливает 1 (сравнение неупорядоченное, тихое)

  • 1Ah: если первый операнд НЕ больше второго, устанавливает 1 (сравнение неупорядоченное, тихое)

  • 1Bh: возвращает 0, если сравнение упорядоченное (сравнение упорядоченное, сигнальное)

  • 1Ch: если операнды не равны, устанавливает 1 (сравнение упорядоченное, сигнальное)

  • 1Dh: если первый операнд больше или равен второму, устанавливает 1 (сравнение упорядоченное, тихое)

  • 1Eh: если первый операнд больше второго, устанавливает 1 (сравнение упорядоченное, тихое)

  • 1Fh: устанавливает 1, если сравнение неупорядоченное, (сравнение неупорядоченное, сигнальное)

Если условие верно, то в соответствующей 32- или 64-битной дорожке XMM0 все биты устанавливаются в 1, если условие не верно, то все биты равны 0.

Например, сравнивним два вектора чисел real4:

.data
    nums0 real4 0.1, 1.2, 2.1, 3.2 
    nums1 real4 0.1, 1.1, 2.1, 3.1 
.code
main proc 
    movaps xmm0, nums0 
    movaps xmm1, nums1
    cmpps xmm0, xmm1, 0
    ;XMM0 = -1, 0, -1, 0
    ret
main endp
end

Здесь сравниваем векторы real4 из регистров XMM0 и XMM1. Поскольку инструкции cmpps в качестве третьего операнда передается число 0 (или 00h), то инструкция проверяет числа в сооветствующих дорожках регистров на равенство. То есть единовременно сравниваем 4 числа из XMM0 с 4 числами из XMM1. Результат сравнения помещается в XMM0 и он будет представлять вектор чисел [-1, 0, -1, 0]. То есть первые и третьи 32-битные дорожки регистров равны, а вторые и четвертые дорожки не равны.

Для ряда случаев есть синонимичные инструкции, которые упрощают понимание кода:

  • cmpeqps x1, x2: синоним для cmpps x1, x2, 0

  • cmpeqpd x1, x2: синоним для cmppd x1, x2, 0

  • cmpltps x1, x2: синоним для cmpps x1, x2, 1

  • cmpltpd x1, x2: синоним для cmppd x1, x2, 1

  • cmpleps x1, x2: синоним для cmpps x1, x2, 2

  • cmplepd x1, x2: синоним для cmppd x1, x2, 2

  • cmpunordps x1, x2: синоним для cmpps x1, x2, 3

  • cmpunordpd x1, x2: синоним для cmppd x1, x2, 3

  • cmpneqps x1, x2: синоним для cmpps x1, x2, 4

  • cmpneqpd x1, x2: синоним для cmppd x1, x2, 4

  • cmpnltps x1, x2: синоним для cmpps x1, x2, 5

  • cmpnltpd x1, x2: синоним для cmppd x1, x2, 5

  • cmpnleps x1, x2: синоним для cmpps x1, x2, 6

  • cmpnlepd x1, x2: синоним для cmppd x1, x2, 6

  • cmpordps x1, x2: синоним для cmpps x1, x2, 7

  • cmpordpd x1, x2: синоним для cmppd x1, x2, 7

Так, примеры выше мы могли бы переписать так:

.data
    nums0 real4 0.1, 1.2, 2.1, 3.2 
    nums1 real4 0.1, 1.1, 2.1, 3.1 
.code
main proc 
    movaps xmm0, nums0 
    movaps xmm1, nums1
    cmpeqps xmm0, xmm1
    ;XMM0 = -1, 0, -1, 0
    ret
main endp
end

Версии AVX этих инструкций принимают 4 операнда: целевой регистр XMM или YMM для хранения результатов сравнения, два сравниваемых операнда и операнд imm8, который указывает на тип сравнения:

vcmpps xmmdest, xmmsrc1, xmmsrc2/mem128, imm8
vcmpps ymmdest, ymmsrc1, ymmsrc2/mem256, imm8
vcmppd xmmdest, xmmsrc1, xmmsrc2/mem128, imm8
vcmppd ymmdest, ymmsrc1, ymmsrc2/mem256, imm8

В зависимости от значения четверного операнда - imm8 для этих инструкций есть синонимичные инструкций:

imm8

Инструкция

00h

vcmpeqps и vcmpeqpd

01h

vcmpltps и vcmpltpd

02h

vcmpleps и vcmplepd

03h

vcmpunordps и vcmpunordpd

04h

vcmpneqps и vcmpneqpd

05h

vcmpltps и vcmpltpd

06h

vcmpleps и vcmplepd

07h

vcmpordps и vcmpordpd

08h

vcmpeq_uqps и vcmpeq_uqpd

09h

vcmpngeps и vcmpngepd

0Ah

vcmpngtps и vcmpngtpd

0Bh

vcmpfalseps и vcmpfalsepd

0Ch

vcmpneq_oqps и vcmpneq_oqpd

0Dh

vcmpgeps и vcmpgepd

0Eh

vcmpgtps и vcmpgtpd

0Fh

vcmptrueps и vcmptruepd

10h

vcmpeq_osps и vcmpeq_ospd

11h

vcmplt_oqps и vcmplt_oqpd

12h

vcmple_oqps и vcmple_oqpd

13h

vcmpunord_sps и vcmpunord_spd

14h

vcmpneq_usps и vcmpneq_uspd

15h

vcmpnlt_uqps и vcmpnlt_uqpd

16h

vcmpnle_uqps и vcmpnle_uqpd

17h

vcmpord_sps и vcmpord_spd

18h

vcmpeq_usps и vcmpeq_uspd

19h

vcmpnge_uqps и vcmpnge_uqpd

1Ah

vcmpngt_uqps и vcmpngt_uqpd

1Bh

vcmpfalse_osps и vcmpfalse_ospd

1Ch

vcmpneq_osps и vcmpneq_ospd

1Dh

vcmpge_oqps и vcmpge_oqpd

1Eh

vcmpgt_oqps и vcmpgt_oqpd

1Fh

vcmptrue_usps и vcmptrue_uspd

Получение результатов сравнения

Инструкции movmskps и movmskpd извлекают знаковые биты 32- и 64-битных дорожек регистров XMM и YMM соответственно и сохраняют эти биты в младших 4 или 8 битах регистра общего назначения.

movmskps reg, xmmsrc
movmskpd reg, xmmsrc
vmovmskps reg, ymmsrc
vmovmskpd reg, ymmsrc

Первый операнд представляет 32- или 64-битный регистр общего назначения. Второй операнд представляет регистр XMM или YMM с результатами сравнения.

Инструкция movmskps копирует знаковые биты из четырех дорожек регистра XMM в младшие 4 бита регистра общего назначения

Сравнение в SIMD в ассемблере

Инструкция movmskpd копирует знаковые биты из четырех дорожек регистра XMM в биты 0 и 1 регистра общего назначения.

Инструкция vmovmskps копирует знаковые биты из четырех дорожек регистра XMM или из восьми дорожек регистра YMM в малдшие 4 и 8 битов регистра назначения соответственно.

Инструкция vmovmskpd копирует знаковые биты из четырех дорожек регистра YMM в младшие 4 биты регистра общего назначения.

Стоит отметить, что хотя эти инструкции ориентированы прежде всего на сравнение чисел с плавающей точкой - real4 и real8, но их также можно использовать для получения результатов сравнения целых чисел (movmskps и vmovmskps) и 64-битных целых чисел (movmskpd и vmovmskpd). Например:

.data
    nums0 qword 23, 55
    nums1 qword 22, 55
.code
main proc 
    movapd xmm0, nums0 
    movapd xmm1, nums1
    cmpeqpd xmm0, xmm1  ; XMM0 = XMM0 == XMM1  (XMM0 =  -1, 0)
    movmskpd rax, xmm0  ; RAX = 2 (010b - в двоичной системе)
    ret
main endp
end

В данном случае проверяются на равенство элементы двух векторов четверных слов, и с помощью инструкции movmskpd результаты сравнения помещаются в регистр RAX. Поскольку сравнивают два значения, то в регистре RAX результаты будут помещаться в два первых бита. В итоге в RAX будет число 2 или 10b в бинарной системе. Первый бит равен 0, так как числа первых дорожек векторов не равны. Второй бит равен 1, так как вторые дорожки векторов равны.

Получив результаты сравнения в регистр общего назначения, очень удобно использовать данный регистр для условной обработки:

.data
    nums0 qword 23, 55
    nums1 qword 22, 55
.code
main proc 
    movapd xmm0, nums0 
    movapd xmm1, nums1
    cmpeqpd xmm0, xmm1  ; XMM0 = XMM0 == XMM1
    movmskpd rax, xmm0  ; RAX = 2 (010b - в двоичной системе)
    lea rcx, jmpTable           ; загружаем адрес таблицы jmpTable
    jmp qword ptr [rcx][rax*8]  ; в зависимости от результата переходим к определенной метке
    
    jmpTable qword nene     ; обе пары чисел не равны
            qword neeq      ; в первой паре  числа равны, во второй не равны
            qword eqne      ; во второй паре  числа равны, первой не равны
            qword eqeq      ; обе пары чисел равны
nene:
    mov rax, 4
    jmp exit
neeq:
    mov rax, 8
    jmp exit
eqne:
    mov rax, 16
    jmp exit
eqeq:
    mov rax, 32
    jmp exit
exit:
    ret
main endp
end

Так как инструкция movmskpd копирует 2 бита из XMM0 в RAX, можно использовать RAX в качестве индекса в таблице переходов для выбора одной из четырех разных меток. Код с меткой nene выполняется, если в обоих парах числа не равны. Метка neeq определяет код, если в первой паре числа равны, во второй не равны. Метка eqne определяет код для ситуации, если во второй паре числа равны, а в первой не равны. И метка eqeq определяет код для ситуации, когда оба набора дорожек содержат одинаковые значения.

Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850