Можно ли использовать PTEST для проверки того, что два регистра равны нулю, или какое-то другое условие?
Что вы можете сделать с SSE4.1ptest
кроме тестирования, если единственный регистр - все ноль?
Можете ли вы использовать комбинацию SF и CF, чтобы проверить что-нибудь полезное о двух неизвестных входных регистрах?
Для чего нужен PTEST? Вы могли бы подумать, что это было бы хорошо для проверки результата упакованного сравнения (например, PCMPEQD или CMPPS), но, по крайней мере, на процессорах Intel, сравнение и ответвление с использованием PTEST + JCC обходится дороже, чем с PMOVMSK(B/PS/PD) + макро-слитый CMP + JCC.
См. Также Проверка, не являются ли два регистра SSE ненулевыми, не уничтожая их
1 ответ
Нет, если я не пропущу что-нибудь умное, ptest
с двумя неизвестными регистрами, как правило, бесполезно для проверки некоторых свойств обоих. (Кроме очевидных вещей, для которых вы уже хотели бы использовать побитовое И, например, пересечение между двумя растровыми изображениями).
Чтобы проверить два регистра на то, что оба являются нулями, ИЛИ их вместе и PTEST, что против себя.
ptest xmm0, xmm1
дает два результата:
- ZF = есть
xmm0 & xmm1
все-ноль? - CF = есть
(~xmm0) & xmm1
все-ноль?
Если второй вектор все ноль, флаги не зависят вообще от битов в первом векторе.
Может быть полезно думать о проверках "все равно нулю" как NOT(bitwise horizontal-OR())
результатов AND и ANDNOT. Но, вероятно, нет, потому что это слишком много шагов, чтобы мой мозг мог легко обдумать. Эта последовательность вертикальных И, а затем горизонтальных ИЛИ, возможно, облегчает понимание того, почему PTEST не говорит вам много о комбинации двух неизвестных регистров, как целочисленная инструкция TEST.
Вот таблица истинности для 2-битной ptest a,mask
, Надеемся, что это помогает думать о миксах из нулей и единиц с входами 128b.
Обратите внимание, что CF(a,mask) == ZF(~a,mask)
,
a mask ZF CF
00 00 1 1
01 00 1 1
10 00 1 1
11 00 1 1
00 01 1 0
01 01 0 1
10 01 1 0
11 01 0 1
00 10 1 0
01 10 1 0
10 10 0 1
11 10 0 1
00 11 1 0
01 11 0 0
10 11 0 0
11 11 0 1
Руководство Intel по внутренним характеристикам перечисляет 2 интересных свойства для него. Обратите внимание на названия аргументов: a
а также mask
являются подсказкой, что они рассказывают вам о частях a
выбирается по известной И-маске.
_mm_test_mix_ones_zeros (__m128i a, __m128i mask)
: возвращается(ZF == 0 && CF == 0)
_mm_test_all_zeros (__m128i a, __m128i mask)
: возвращаетсяZF
Есть также более простые версии:
int _mm_testc_si128 (__m128i a, __m128i b)
: возвращаетсяCF
int _mm_testnzc_si128 (__m128i a, __m128i b)
: возвращается(ZF == 0 && CF == 0)
int _mm_testz_si128 (__m128i a, __m128i b)
: возвращаетсяZF
Есть AVX2 __m256i
версии этих встроенных функций, но в руководстве перечислены только версии альтернативных имен all_zeros и mix_ones_zeros для __m128i
операнды.
Если вы хотите проверить какое-либо другое условие из C или C++, вы должны использовать testc
а также testz
с теми же операндами, и надеемся, что ваш компилятор поймет, что ему нужно сделать только один PTEST, и, мы надеемся, даже использовать один JCC, SETCC или CMOVCC для реализации вашей логики. (Я бы порекомендовал проверить asm, по крайней мере, для компилятора, который вас больше всего интересует.)
Обратите внимание, что _mm_testz_si128(v, set1(0xff))
всегда так же, как _mm_testz_si128(v,v)
потому что так работает И Но это не относится к результату CF.
Вы можете проверить, что вектор является единым, используя
bool is_all_ones = _mm_testc_si128(v, _mm_set1_epi8(0xff));
Это, вероятно, не быстрее, но меньший размер кода, чем PCMPEQB против вектора из всех единиц, тогда как обычная movemask + cmp. Это не избавляет от необходимости векторной константы.
Преимущество PTEST заключается в том, что он не уничтожает ни один из операндов ввода, даже без AVX.