Каждый байт является маской для всего двойного числа, , поэтому PMOVSXBQ
делает именно то, что нам нужно : загружает два байта из указателя m16
и расширяет их до двух 64-битных (qword) ) половины регистра xmm.
# UNTESTED CODE
# (loop setup stuff)
# RSI: double pointer
# RDI: mask pointer
# RCX: loop conter = mask byte-count
add rdi, rcx
lea rsi, [rsi + rcx*8] ; sizeof(double) = 8
neg rcx ; point to the end and count up
XORPS xmm0, xmm0 ; clear accumulator
; for real use: use multiple accumulators
; to hide ADDPD latency
ALIGN 16
.loop:
PMOVSXBQ XMM1, [RDI + RCX]
ANDPD XMM1, [RSI + RCX * 8]
ADDPD XMM0, XMM1
add RCX, 2 ; 2 bytes / doubles per iter
jl .loop
MOVHLPS XMM1, XMM0 ; combine the two parallel sums
ADDPD XMM0, XMM1
ret
Для реального использования используйте несколько аккумуляторов. Также см. Режимы микросинтеза и адресации re: индексированные режимы адресации.
Написание этого с помощью встроенных функций должно быть простым. Как уже отмечали другие, просто используйте разыменованные указатели в качестве аргументов для встроенных функций.
Чтобы ответить на другую часть вашего вопроса, о , как сдвинуть данные, чтобы выстроить их в соответствие для PMOVSX
:
В Sandybridge и более поздних версиях использование PMOVSXBQ из ОЗУ, вероятно, хорошо. На более ранних процессорах, которые не могут обрабатывать две загрузки за цикл, загрузка 16B данных маски за раз и смещение их на 2 байта за раз с PSRLDQ xmm1, 2
поместит 2 байта данных маски в младшие 2 байта регистра , Или, может быть, PUNPCKHQDQ
, или PSHUFD
, чтобы получить две цепочки зависимостей, перемещая максимум 64 в минимум 64 другого регистра. Вам нужно проверить, какой порт используется какой инструкцией (сдвиг вместо случайного / извлечения), и посмотреть, какой из них меньше конфликтует с PMOVSX
и ADDPD
.
punpck
и pshufd
оба используют p1 / p5 на SnB, как и pmovsx
. addpd
может работать только на p1. andpd
может работать только на p5. Хм, может быть, PAND
будет лучше, так как он может работать на p0 (и p1 / p5). Иначе ничто в цикле не будет использовать порт выполнения 0. Если есть штраф за задержку при перемещении данных из целочисленных доменов в домены fp, это неизбежно, если мы используем PMOVSX
, так как это получит данные маски в домене int. Лучше использовать больше аккумуляторов, чтобы сделать цикл длиннее, чем самая длинная цепочка зависимостей. Но держите его под 28 моп или около того, чтобы поместиться в буфер цикла, чтобы гарантировать, что 4 мопа могут выдать за цикл.
И еще об оптимизации всего этого:
Выравнивание цикла на самом деле не требуется, так как в Nehalem и более поздних версиях он помещается в буфер цикла.
Вы должны развернуть цикл на 2 или 4, потому что процессорам Intel до Haswell не хватает исполнительных блоков для обработки всех 4 (слитых) мопов за один цикл. (3 вектора и один слитый add
/ jl
. Две нагрузки сливаются с векторными мопами, частью которых они являются.) Sandybridge и более поздние версии могут выполнять обе загрузки каждый цикл, поэтому выполнима одна итерация за цикл, кроме заголовка цикла .
О, ADDPD
имеет задержку в 3 цикла. Таким образом, вам необходимо развернуть и использовать несколько аккумуляторов, чтобы избежать наличия узкой места в цепочке зависимостей, переносимых циклами. Вероятно, разверните на 4, а затем суммируйте 4 аккумулятора в конце. Вам придется делать это в исходном коде даже с встроенными функциями, потому что это изменит порядок операций для математики FP, поэтому компилятор может не захотеть делать это при развертывании.
Таким образом, каждый развернутый циклом 4 будет занимать 4 такта, плюс 1 моп для накладных расходов цикла. На Nehalem, где у вас есть крошечный циклический кэш, но нет кэша UOP, развертывание может означать, что вы должны начать заботиться о пропускной способности декодера. Однако на предварительном песчаном мосту одна нагрузка на часы в любом случае, вероятно, станет узким местом.
Для пропускной способности декодера вы, вероятно, можете использовать ANDPS
вместо ANDPD
, для кодирования которого требуется на один байт меньше. ИДК, если это поможет.
Расширение этого до 256b ymm
регистров потребует AVX2 для наиболее простой реализации (для VPMOVSXBQ ymm
). Вы можете ускорить работу только с AVX, сделав два VPMOVSXBQ xmm
и комбинируя их с VINSERTF128
или чем-то еще.