Можно ли использовать PTEST для проверки того, являются ли два регистра нулевыми или другими условиями?
что вы можете сделать с SSE4.1 ptest
кроме тестирования, если один регистр все-ноль?
можете ли вы использовать комбинацию SF и CF для проверки чего-либо полезного о двух неизвестных входных регистрах?
для чего хорош PTEST? Вы думаете, что это было бы хорошо для проверки результата упакованного сравнения (например, PCMPEQD или CMPPS), но, по крайней мере, на процессорах Intel,это стоит больше uops для сравнения и ветви с использованием PTEST + JCC, чем с PMOVMSK (B/PS/PD) + макро-плавленый СМР+СКК.
см. также проверка, если два регистра SSE не оба нуля, не уничтожая их
1 ответов
нет, если только я не пропустил что-то умное, ptest
С двумя неизвестными регистрами обычно не полезно для проверки некоторого свойства о них обоих. (Кроме очевидных вещей, которые вы уже хотели бы побитового-и для, например, пересечения двух растровых изображений).
проверить два регистра на то, что оба являются all-zero, или их вместе и PTEST, что против себя.
ptest xmm0, xmm1
выдает два результата:
- ZF = is
xmm0 & xmm1
все-ноль? - CF = is
(~xmm0) & xmm1
все-ноль?
если второй вектор равен нулю, флаги вообще не зависят от битов в первом векторе.
может быть полезно думать о проверках "is-all-zero" как NOT(bitwise horizontal-OR())
результатов AND и ANDNOT. Но, вероятно, нет, потому что это слишком много шагов для моего мозга, чтобы легко думать. Эта последовательность вертикальных-и затем горизонтальных-или, возможно, облегчает понимание почему PTEST не говорит вам много о комбинации двух неизвестных регистров, как и целочисленная тестовая инструкция.
Вот таблица правды для 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 интересных встроенных устройства для него. Обратите внимание на название args:a
и mask
подсказка, что они расскажите о частях a
выбирается известным и-маской.
-
_mm_test_mix_ones_zeros (__m128i a, __m128i mask)
: returns(ZF == 0 && CF == 0)
-
_mm_test_all_zeros (__m128i a, __m128i mask)
: returnsZF
есть также более просто названные версии:
-
int _mm_testc_si128 (__m128i a, __m128i b)
: returnsCF
-
int _mm_testnzc_si128 (__m128i a, __m128i b)
: returns(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.
вы можете проверить наличие вектора, являющегося all-ones используя
bool is_all_ones = _mm_testc_si128(v, _mm_set1_epi8(0xff));
это, вероятно, не быстрее, но меньший размер кода, чем PCMPEQB против вектора all-ones, а затем обычный movemask + cmp. Это не исключает необходимости постоянной вектора.
PTEST имеет преимущество в том, что он не уничтожает ни один входной операнд, даже без AVX.