2017-02-18 4 views
4

Приятно, что PTEST не влияет на флаг переноса, но устанавливает только (довольно неудобный) ZF. также влияет как на CF, так и на ZF.Более быстрый способ проверить, равен ли регистр xmm/ymm нулю?

Я придумал следующую последовательность, чтобы проверить большое количество значений, но я недоволен плохим временем работы.

   Latency/rThoughput 
setup: 
    xor eax,eax  ; na 
    vpxor xmm0,xmm0 ; na  ;mask to use for the nand operation of ptest 
work: 
    vptest xmm4,xmm0 ; 3 1 ;is xmm4 alive? 
    adc eax,eax  ; 1 1 ;move first bit into eax 
    vptest xmm5,xmm0 ; 3 1 ;is N alive? 
    adc eax,eax  ; 1 1 ;move consecutive bits into eax 

Я хочу, чтобы растровое изображение всех ненулевых регистров в eax (очевидно, я могу объединить несколько растровых изображений в разных регистрах).

Таким образом, каждый тест имеет задержку 3 + 1 = 4 цикла.
Некоторые из них могут работать параллельно, чередуясь между eax, ecx и т. Д.
Но это все еще довольно медленно.
Есть ли более быстрый способ сделать это?

Мне нужно проверить 8 xmm/ymm регистров подряд. 1 бит на регистр в однобайтовом растровом изображении.

+1

Какой желаемый выход? Вы хотите иметь растровое изображение, значение которого отличное от нуля? Или достаточно знать, отличен ли какой-либо регистр? – fuz

+0

Конечно, многое зависит от окружающего кода: сколько регистров вы будете сравнивать в нуле в цикле и что вы хотите сделать с результатом? Без ограничений на выходной формат «самый быстрый» способ проверить, равен ли регистр нулю, использовать одну из инструкций «PCMPEQ» для сравнения с нулевым регистром, но это может не оставить результат в форме, которую вы хотите. .. ** Добавлено: ** fuz избил меня к ней на минутку :) – BeeOnRope

+0

Сколько регистров вы тестируете в строке ниже? – BeeOnRope

ответ

6

Вместо того, чтобы быть «довольно медленным», ваш существующий подход на самом деле разумный.

Конечно каждый отдельный тест имеет латентности из 4 циклов , но если вы хотите, чтобы результат в регистр общего назначения вы обычно будете платить Латентность 3 цикла для этого двигаться в любом случае (например, movmskb также имеет задержку 3). В любом случае, вы хотите протестировать 8 регистров, и вы не просто добавляете задержки, потому что каждый из них в основном независим, поэтому подсчет uop и использование порта, скорее всего, будут более важными, чем латентность для проверки одного регистра как самого латентности будут перекрываться с другой работой.

. При использовании нескольких последовательных инструкций PCMPEQ подход, который может быть немного быстрее на аппаратных средствах Intel, позволяет проверить несколько векторов, а затем складывать результаты вместе (например, если вы используете PCMPEQQ, у вас есть 4 результата с четырьмя словами, и вам необходимо и сложить их в 1). Вы можете складывать до или после PCMPEQ, но это поможет узнать больше о том, как/где вы хотите получить результаты, чтобы придумать что-то лучшее. Вот непроверенный эскиз для 8 регистров, xmm1-8 с xmm0 Предполагаемый нуль, а xmm14 является маской pblendvb, чтобы выбрать альтернативные байты, используемые в последней инструкции.

# test the 2 qwords in each vector against zero 
vpcmpeqq xmm11, xmm1, xmm0 
vpcmpeqq xmm12, xmm3, xmm0 
vpcmpeqq xmm13, xmm5, xmm0 
vpcmpeqq xmm14, xmm7, xmm0 

# blend the results down into xmm10 word origin 
vpblendw xmm10, xmm11, xmm12, 0xAA # 3131 3131 
vpblendw xmm13, xmm13, xmm14, 0xAA # 7575 7575 
vpblendw xmm10, xmm10, xmm13, 0xCC # 7531 7531 

# test the 2 qwords in each vector against zero 
vpcmpeqq xmm11, xmm2, xmm0 
vpcmpeqq xmm12, xmm4, xmm0 
vpcmpeqq xmm13, xmm6, xmm0 
vpcmpeqq xmm14, xmm8, xmm0 

# blend the results down into xmm11 word origin 
vpblendw xmm11, xmm11, xmm12, 0xAA # 4242 4242 
vpblendw xmm13, xmm13, xmm14, 0xAA # 8686 8686 
vpblendw xmm11, xmm11, xmm13, 0xCC # 8642 8642 

# blend xmm10 and xmm11 together int xmm100, byte-wise 
#   origin bytes 
# xmm10 77553311 77553311 
# xmm11 88664422 88664422 
# res 87654321 87654321 
vpblendvb xmm10, xmm10, xmm11, xmm15 

# move the mask bits into eax 
vpmovmskb eax, xmm10 
and al, ah 

Интуиция является то, что вы проверить каждый QWORD в каждом xmm против нуля, что дают 16 результаты для 8 регистров, а затем смешать результаты вместе в xmm10 в конечном итоге с одного результата за байт, чтобы (с все результаты с высоким QWORD перед всеми результатами с низким QWORD). Затем вы перемещаете эти 16-байтовые маски как 16-битные в eax с movmskb и, наконец, объединяете биты верхнего и нижнего QWORD для каждого регистра внутри eax.

Это выглядит так, как мне кажется, всего 16 юаней, для 8 регистров, примерно 2 раза за регистр. Общая латентность является разумной, поскольку она в основном представляет собой параллельное дерево типа «уменьшить». Ограничивающим фактором будет 6 vpblendw операций, которые все идут только на порт 5 на современной Intel. Лучше было бы заменить 4 из них VPBLENDD, который является «благословенной» смесью, которая идет на любой из p015. Это должно быть просто.

Все операционные системы простые и быстрые. Финал and al, ah - это частичный регистр записи, но если вы mov после этого в eax, возможно, штраф отсутствует. Вы также можете сделать эту последнюю пару несколькими способами, если это проблема ...

Этот подход также естественно масштабируется до ymm регистров, с немного отличающимся от складывания в eax в конце.

EDIT

Чуть быстрее заканчивается использует упакованные сдвиги, чтобы избежать двух дорогих инструкции:

;combine bytes of xmm10 and xmm11 together into xmm10, byte wise 
; xmm10 77553311 77553311 
; xmm11 88664422 88664422 before shift 
; xmm10 07050301 07050301 
; xmm11 80604020 80604020 after shift 
;result 87654321 87654321 combined 
vpsrlw xmm10,xmm10,8 
vpsllw xmm11,xmm11,8 
vpor xmm10,xmm10,xmm11 

;combine the low and high dqword to make sure both are zero. 
vpsrldq xmm12,xmm10,64 
vpand xmm10,xmm12 
vpmovmskb eax,xmm10 

Это экономит 2 цикла, избегая 2 цикла vpblendvb и частичной записи штраф or al,ah, он также фиксирует зависимость от медленного vpmovmskb, если вам не нужно сразу использовать результат этой инструкции.


На самом деле, кажется, только на Skylake что PTEST имеет задержку трех циклов, до того, что это, кажется, 2. Я также не уверен, что задержки в 1 цикл, указанный вами rcl eax, 1 : по словам Агнера, на современной Intel, похоже, 3 часа и 2 цикла латентности/пропускной способности.

+0

О, я смотрел на тайминги AMD, я думаю, что 'adc eax, eax' - лучший выбор, чем. – Johan

+1

Не следует ли 'pblendvb xmm10, xmm10, xmm11, xmm14' быть' vpblendvb xmm0, xmm10, xmm11, xmm14'? –

+1

Правильно, да! На самом деле «movmskb» предназначался для использования 'xmm10' не' xmm0' (так как приятно сохранять обнуленный аспект 'xmm0', возможно, для следующего кода). – BeeOnRope

Смежные вопросы