Вместо того, чтобы быть «довольно медленным», ваш существующий подход на самом деле разумный.
Конечно каждый отдельный тест имеет латентности из 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 цикла латентности/пропускной способности.
Какой желаемый выход? Вы хотите иметь растровое изображение, значение которого отличное от нуля? Или достаточно знать, отличен ли какой-либо регистр? – fuz
Конечно, многое зависит от окружающего кода: сколько регистров вы будете сравнивать в нуле в цикле и что вы хотите сделать с результатом? Без ограничений на выходной формат «самый быстрый» способ проверить, равен ли регистр нулю, использовать одну из инструкций «PCMPEQ» для сравнения с нулевым регистром, но это может не оставить результат в форме, которую вы хотите. .. ** Добавлено: ** fuz избил меня к ней на минутку :) – BeeOnRope
Сколько регистров вы тестируете в строке ниже? – BeeOnRope