Можно ли использовать 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): returns ZF

есть также более просто названные версии:

  • int _mm_testc_si128 (__m128i a, __m128i b): returns CF
  • 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.