Я постараюсь помочь с забытой Может ли кто-нибудь объяснить эту часть вопроса.
register __m128i xmm0, xmm1;
register unsigned int eax;
Здесь мы объявляем несколько переменных. __m128i
- это встроенный тип для целочисленных операций с регистрами SSE. Обратите внимание, что имена переменных не имеют никакого значения, но автор назвал их в точности так, как соответствующие регистры ЦП вызываются в сборке. xmm0
, xmm1
, xmm2
, xmm3
, ... все регистры для операций SSE. eax
- один из регистров общего назначения.
Ключевое слово register
использовалось давно, чтобы посоветовать компилятору поместить переменную в регистр процессора. Думаю, сегодня это совершенно бесполезно. Дополнительные сведения см. В этом вопросе.
xmm0 = _mm_loadu_si128((__m128i*)(a));
xmm1 = _mm_loadu_si128((__m128i*)(b));
Этот код был изменен в соответствии с предложением @harold. Здесь мы загружаем 16 байтов из указанных указателей памяти, которые могут быть невыровненными) в переменные xmm0
и xmm1
. В ассемблерном коде эти переменные, скорее всего, будут расположены непосредственно в регистрах, поэтому эти встроенные функции будут генерировать невыровненную нагрузку на память. Преобразование указателя в тип __m128i*
необходимо, потому что intrinsic принимает этот тип указателя, хотя я понятия не имею, почему Intel это сделала.
xmm0 = _mm_cmpeq_epi8(xmm0, xmm1);
Здесь мы сравниваем на равенство каждый байт из переменной xmm0
с соответствующим байтом в переменной xmm1
. Суффикс _epi8
означает работу с 8-битными элементами, то есть байтами. Он чем-то похож на memcmp(&xmm0, &xmm1, 16)
, но дает другие результаты. Он возвращает 16-байтовое значение, которое содержит 0xFF
для каждого байта с равными значениями и 0x00
для каждого байта с разными значениями.
eax = _mm_movemask_epi8(xmm0);
Это очень важная инструкция из SSE2, которая обычно используется для написания оператора if
с некоторым условием SSE. Он берет старший бит из каждого из 16 байтов в аргументе XMM и записывает их в одно 16-битное целое число. На уровне сборки этот номер находится в универсальном регистре, что позволяет нам быстро проверить его значение сразу после этого.
if(eax==0xffff) //equal
else //not equal
Если все 16 байтов двух регистров XMM были равны, то _mm_cmpeq_epi8
должен возвращать маску со всеми установленными 128 битами. _mm_movemask_epi8
тогда вернет полную 16-битную маску, которая равна 0xFFFF
. Если бы любые два сравниваемых байта были разными, соответствующий байт был бы заполнен нулями на _mm_cmpeq_epi8
, поэтому _mm_movemask_epi8
вернет 16-битную маску с соответствующим битом не, поэтому он будет меньше 0xFFFF
.
Кроме того, вот объясненный код, заключенный в функцию:
bool AreEqual(const char *a, const char *b) {
__m128i xmm0, xmm1;
unsigned int eax;
xmm0 = _mm_loadu_si128((__m128i*)(a));
xmm1 = _mm_loadu_si128((__m128i*)(b));
xmm0 = _mm_cmpeq_epi8(xmm0, xmm1);
eax = _mm_movemask_epi8(xmm0);
return (eax == 0xffff); //equal
}
29.08.2015
ptest
использует 2 мупа на текущих архитектурах Intel. Более того, он не связан с условным переходом после него. Таким образом, ваше решение генерирует 4 мопа, а код OP генерирует только 3 мопа. Дополнительные сведения см. В этом вопросе. Предположительно это означает, что ваше решение будет медленнее в жестком цикле. 27.08.2015PTEST
может иметь меньшую задержку (меньший штраф за неверный прогноз), но я думаю, что вы правы, в этом случаеpcmpeq/pmovmskb/ cmp/jne
меньше мопов, потому чтоptest
не может сравнивать-равно напрямую. Груз может складываться как вpcmpeq
, так и вpxor
. Я также не уверен в штрафах за неверный прогноз: я только что посмотрел на таблицы задержки Агнера Фога, а не на эксперименты. 27.08.2015pmovmskb
наptest
. Кажется, что инструкцияptest
может быть полезна только в том случае, если вам абсолютно необходимо проверить все 128 бит регистра на ноль (что случается очень редко). Если вы проводите какое-либо сравнение (что является наиболее популярным случаем), то использованиеptest
после него вредно. Ну, может, у него меньшая задержка, кто знает ... 29.08.2015pcmpeq / ptest
может проверять только все - не - равно. Вот почему я использовалpxor / ptest
, чтобы сохранить ту же семантику, что и код OP. Однакоpcmpeq
должен быть таким же быстрым, какpxor
на Intel.ptest
работает на p0 и p5 (с задержкой 2c).pcmpeqb
может работать на порту 1 или 5, поэтому он может использовать порт, которого неptest
. (pxor
может работать на всех 3-х векторных портах выполнения, p0 / 1/5.) Ваш тест мог бы иметь меньше ошибочных прогнозов ветвлений, чем вы ожидали, если бы у вас была другая семантика. 29.08.2015ptest
не хуже в других случаях, но для меня сейчас очень сомнительно, можно ли его использовать. 29.08.2015ptest
, я подумал, что обычно они были равными, но потенциально более низкими задержками. (кроме этого случая, когда я запутался и предложил это в случае, когда это больше упс. ›.‹) 29.08.2015