Каждый байт является маской для целого числа double, поэтому 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 из ОЗУ, вероятно, хорошо. На более ранних процессорах, которые не могли обрабатывать две загрузки за цикл, загрузка 16 Б данных маски за раз и сдвиг их на 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 вектора и одна объединенная _17 _ / _ 18_. Две нагрузки объединяются с векторными мопами, частью которых они являются.) Sandybridge и более поздние версии могут выполнять обе загрузки в каждом цикле, поэтому возможна одна итерация за цикл, за исключением накладных расходов на цикл.
О, ADDPD
имеет задержку в 3 цикла. Таким образом, вам нужно развернуть и использовать несколько аккумуляторов, чтобы цепочка зависимостей, переносимая по циклу, не была узким местом. Вероятно, разверните на 4, а затем суммируйте 4 аккумулятора в конце. Вам придется сделать это в исходном коде даже с помощью встроенных функций, потому что это изменит порядок операций для математики FP, поэтому компилятор может не захотеть делать это во время развертывания.
Таким образом, каждый цикл, развернутый на 4, потребует 4 тактовых цикла плюс 1 муп на накладные расходы цикла. В Nehalem, где у вас есть крошечный кеш цикла, но нет кеша uop, развертывание может означать, что вам нужно начать заботиться о пропускной способности декодера. Однако на «до-песчаном мосту» одна загрузка за такт, вероятно, в любом случае будет узким местом.
Для пропускной способности декодера вы, вероятно, можете использовать ANDPS
вместо ANDPD
, что требует для кодирования на один байт меньше. IDK, если это поможет.
Расширение этого числа до 256 ymm
регистров потребует AVX2 для наиболее простой реализации (для VPMOVSXBQ ymm
). Вы можете получить ускорение на AVX-only, выполнив два VPMOVSXBQ xmm
и объединив их с VINSERTF128
или чем-то в этом роде.
person
Peter Cordes
schedule
04.06.2015
pmovsxbq
может фактически взять операнд памяти и напрямую загрузить эти два байта из памяти. Но, конечно, команду MSVC это не волнует. - person harold   schedule 01.04.2012pmovsxbq
во встроенной сборке. В противном случае можно прочитать сразу 16 байт и несколькоpshufb
, чтобы переместить байты в нужные места. - person Gunther Piez   schedule 01.04.2012gcc
иicc
решают поступать правильно. - person FrankH.   schedule 05.04.2012