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

Другие вопросы по тегам