См. Руководство по оптимизации ассемблера Агнера Фога , в его главе о SIMD есть таблица инструкций перемешивания различных типов перемещения данных, которая даст вам небольшое количество инструкций для размышления (или поиска в руководствах Intelесли вы точно не помните, что они делают) и посмотрите, что вы хотите.
Самый дешевый способ передать высокое слово регистра на оба элемента - movhlps xmm0,xmm0
. (Или для целочисленных данных, если ваш код может работать на Nehalem, используйте punpckhqdq xmm0,xmm0
, чтобы избежать задержек обхода FP <-> vec-int.)
Без AVX, movhlps
хорошо, потому чтоон немного отличается от случая, когда unpckhpd
.
movhlps xmm3, xmm4
делает xmm3[0] = xmm4[1];
, оставляя xmm3[1]
без изменений. unpckhpd xmm3, xmm4
занимаетвысокие слова из xmm3 и xmm4 и помещает их в xmm3 в указанном порядке.Таким образом, в пункте назначения высокое ключевое слово перемещается в низкое, а затем высокое ключевое слово из источника копируется.xmm3[0] = xmm3[1]; xmm3[1] = xmm4[1]
Но unpcklpd
бесполезен, он на 1 байт длиннее и делает то же самое, что SSE1 movlhps
.(скопируйте младшее слово из src в старшее слово назначения, оставив младшее слово назначения без изменений.) То же самое для movapd
, всегда используйте вместо movaps
.
Также re: code-size: стоит использовать префикс REX для использования xmm8..15, поэтому выберите распределение регистров, чтобы использовать xmm8..15 как можно меньше инструкций (или тех, которые уже требуют префикс REX, например, для указателя в r8..15),Размер кода, как правило, не имеет большого значения, но все остальное равно меньше, как правило, лучше.Меньшие инструкции обычно лучше упаковываются в кэш uop.
С AVX вы можете использовать vunpckhpd
с любым порядком исходных операндов , причем старшее ключевое слово первого src направляется книзкое слово назначения.Для vmovhlps
нет никакого преимущества в размере кода (или другого преимущества), они оба могут использовать 2-байтовый префикс VEX для минимального размера инструкции 4 байта.
Например, vunpckhpd xmm0, xmm1, xmm0
похоже на vmovhlps xmm0, xmm0,xmm1
.
Вы можете использовать shufpd
или vpshufd
для решения проблемы, которую вы пытаетесь решить.Это пустая трата размера кода, потому что он требует немедленного, но, видимо, вы не понимали, что вы можете использовать shufpd xmm0, xmm0, 0b11
, чтобы взять (в следующем порядке):
- низкое слово из
xmm0[1]
(первый операнд src, младший бит непосредственного) - старшее слово из
xmm0[1]
(второй операнд src, старший бит немедленного).
Элемент управления перемешивания может читатьодин и тот же элемент ввода несколько раз.
Интересно, что компилятор NASM будет компилировать VUNPCKHPD только с двумя операндами
NASM позволяет писать такие инструкции, как vaddps xmm0, xmm0, xmm1
как vaddps xmm0, xmm1
, опуская отдельный целевой операнд, когда он совпадает с первым источником.
Я озадачен, потому что эти значения имеют двойную точность, а не одинарную, но это работает.
Все это просто биты / байты, которые нужно скопировать вокруг .Если вы не используете инструкцию вычисления FP (например, как addpd
/ addps
), «тип» не имеет значения.(По наличию или отсутствию раздела «Исключения с плавающей точкой SIMD» в ручном вводе можно узнать, заботится ли он о значении битов как битовой комбинации FP или нет. Например, addps
: https://www.felixcloutier.com/x86/addps#simd-floating-point-exceptions. (Но никаких сюрпризов не бывает. Единственные инструкции, которые действительно заботятся об этом, делают по очень очевидным причинам, например, выполняют вычисления FP или преобразование типов, а не просто копируют данные вокруг.)
Никакие реальные процессоры не заботятся о PSСравнение инструкций по производительности с PD, но некоторые заботятся о vec-int и vec-FP, поэтому, к сожалению, не всегда выгодно использовать pshufd
для копирования и перетасовки данных FP или использовать shufps
как 2-сточ. целое число в случайном порядке.
К сожалению, до AVX512 не было "целочисленных" случайных чисел общего назначения с 2 источниками, только инструкции palignr
и punpck
.И до AVX не было инструкций копирования и перемешивания FP.(И по иронии судьбы, vpermilps
с немедленным является избыточным по сравнению с vshufps dst, same,same, imm8
за исключением загрузки источника памяти + случайного перемешивания, и его следует избегать по соображениям размера кода. В чем смысл инструкции VPERMILPS (_mm_permute_ps)? )
movapd xmm2,xmm0 ;preserve both qwords of xmm0
call Round
movsd [scratch_register+0],xmm0 ; write low qword to memory
movhlps xmm0,xmm2
call Round
Это эффективная перестановка, но, к сожалению, создает ложную зависимость между выводом первого раунда и вводом во второй .Таким образом, два вызова не могут работать параллельно.Вместо этого тасуйте при копировании перед первым вызовом, предпочтительно в регистр, который, как вы знаете, некоторое время был «мертвым» или был частью цепочки зависимостей для значения в xmm0, поэтому должен быть готов перед ним.
movhlps xmm2, xmm0 ; extract high qword for later
call Round ; round the low qword
movaps xmm3, xmm0 ; save the result
movaps xmm0, xmm2 ; set up the arg
call Round ; round the high qword
movlhps xmm3, xmm0 ; re-combine into xmm3
Если у вас не хватает регистров, которых не касается ваша рукописная функция Round, вам не особенно нужна память и она не более эффективна.
В качестве бонуса, все эти *Инструкции 1111 * и movhlps
имеют длину всего 3 байта, и их столько же, сколько инструкций в вашей версии.
Еще один вариант (особенно если ваш ввод был в другом регистре для начала)будет сначала Round
верхняя половина, затем вы можете положить верхнюю половину обратно в xmm0 с помощью movlhps
.
И, кстати, если у вас есть SSE4.1, roundpd
может округлить до ближайшего целого числас Ближайшим, в направлении + -Inf (ceil / floor) или в направлении 0 (усечение).
movsd [scratch_register+8],xmm0 ; write low qword to memory
movupd xmm11,[scratch_register]
Никогда не делайте этого, узкий магазин + широкая перезагрузка - гарантированная пересылка в магазинстанцияLL.(~ 10 циклов дополнительной задержки).
Используйте 16-байтовое выровненное хранилище (например, в стеке в [rsp+8]
или что-то), и
unpckhpd xmm0, [scratch_register]
для загрузки+ shuffle .
К сожалению, Intel разработала инструкции unpck источника памяти плохо, поэтому им требуется 16-байтовый источник памяти, а не только 8 байтов, которые они фактически загружают / используют.Есть несколько случаев, когда