2016-09-01 14 views
6

Инструкции SHLD/SHRD являются инструкциями по сборке для реализации сдвигов с несколькими значениями.SIMD-версии команд SHLD/SHRD

Рассмотрим следующую задачу:

uint64_t array[4] = {/*something*/}; 
left_shift(array, 172); 
right_shift(array, 172); 

Что является наиболее эффективным способом реализации left_shift и right_shift, две функции, которые осуществляет свою деятельность переход на массив из четырех 64-разрядного целого числа без знака, как если бы это был большой 256 бит беззнакового целого?

Самый эффективный способ сделать это с помощью инструкций SHLD/SHRD или лучше (например, версии SIMD) инструкции по современной архитектуре?

+0

Для какой архитектуры вы программируете? Если вы находитесь на x86, у вас могут быть инструкции до SSE3 [edit: as @Ruslan указал, что у вас может быть поддержка AVX/AVX2 в 32-битном режиме] или на x86_64 до AVX2 (если вам не очень повезло и вы программа для AVX512 на большом сопроцессоре Intel). Если вы находитесь на ARM и поддерживаете NEON, есть инструкции по сдвигу SIMD. – Dalton

+1

Зависит от того, является ли это «172» фиксированным или просто примерным значением: как 172 - 21,5 байт, что позволяет сначала перенести содержимое на 21 байт, а затем переместить 11 целевых байтов 4 раза вправо (т. Е. 3x 'shrd') и очистка остальных 21 байта с нулем. Если у вас уже есть значение в регистре, проверьте этот вопрос для многих ресурсов: http://stackoverflow.com/q/25248766/4271923 – Ped7g

+3

@Dalton вы также можете использовать AVX2 в 32-битном режиме (ограничено регистрами '' ymmN'' хотя, как и с 'xmmN'). – Ruslan

ответ

5

В этом ответе я расскажу только о x64.
x86 устарел в течение 15 лет, если вы кодируете в 2016 году, вряд ли имеет смысл застревать в 2000 году.
Все времена соответствуют Agner Fog's instruction tables.

Intel Skylake пример тайминги *
В shld/shrd инструкции довольно медленно на x64.
Даже на Intel skylake у них есть латентность в 4 цикла и используется 4 uops, что означает, что он использует множество исполнительных блоков, а на более старых процессорах они еще медленнее.
Я предполагаю, что вы хотите перейти на переменную сумму, что означает

SHLD RAX,RDX,cl  4 uops, 4 cycle latency. -> 1/16 per bit 

Используя 2 смены + добавить Вы можете сделать это быстрее медленнее.

@Init: 
MOV R15,-1 
SHR R15,cl //mask for later use.  
@Work: 
SHL RAX,cl  3 uops, 2 cycle latency 
ROL RDX,cl  3 uops, 2 cycle latency 
AND RDX,R15  1 uops, 0.25 latency 
OR RAX,RDX  1 uops, 0.25 latency  
//Still needs unrolling to achieve least amount of slowness. 

Обратите внимание, что это только сдвигает 64 бит, потому что RDX не влияет.
Итак, вы пытаетесь бить 4 такта на 64 бит.

//4*64 bits parallel shift. 
//Shifts in zeros. 
VPSLLVQ YMM2, YMM2, YMM3 1uop, 0.5 cycle latency. 

Однако, если вы хотите, чтобы делать то, что делает SHLD вам нужно использовать дополнительные VPSLRVQ и OR, чтобы объединить два результата.

VPSLLVQ YMM1, YMM2, YMM3 1uop, 0.5 cycle latency. 
VPSRLVQ YMM5, YMM2, YMM4 1uop, 0.5 cycle latency. 
VPOR YMM1, YMM1, YMM5 1uop, 0.33 cycle latency. 

Вам необходимо будет чередовать 4 комплекта этих расходов, которые вы стоите (3 * 4) + 2 = 14 YMM-регистров.
В результате я сомневаюсь, что вы выиграете от низкой 0,33 латентности VPADDQ, поэтому я возьму на себя 0,5 латентности.
Это делает 3uops, 1,5-секундную задержку для 256 бит = 1/171 за бит = 0,37 цикла за QWord = 10 раз быстрее, неплохо.
Если вы можете получить 1,33 цикла на 256 бит = 1/192 на бит = 0,33 цикла на QWord = 12 раз быстрее.

'It’s the Memory, Stupid!'
Очевидно, я не добавил в накладных петель и нагрузки/сохраняет в/из памяти.
Накладные расходы на петле крошечные, учитывая правильное выравнивание целей перехода, но память
доступ будет легко быть самым большим замедлением.
Одиночный кеш-промах в основной памяти на Skylake может стоить вам more than 250 cycles1.
Это умное управление памятью, что основные выгоды будут сделаны.
12-кратное ускорение с использованием AVX256 - это небольшой картофель для сравнения.

Я не рассчитываю настройку счетчика сдвига в CL/(YMM3/YMM4), потому что предполагаю, что вы будете использовать это значение для многих итераций.

Вы не будете бить это с помощью инструкций AVX512, потому что процессоры потребительского класса с инструкциями AVX512 пока недоступны.
Единственный текущий процессор, который поддерживает в настоящее время, - Knights Landing.

*) Все эти тайминги являются лучшими значениями для случая и должны восприниматься как показания, а не как жесткие значения.
) Стоимость пропусков кэш-памяти в Skylake: 42 цикла + 52ns = 42 + (52 * 4.6Ghz) = 281 цикл.

+1

Просто для того, чтобы пропустить кеширование памяти на Skylake, не так плохо, как 1000 циклов (если не считать ошибки страницы). Это может произойти только в том случае, если это был промах кэша на очень удаленном узле NUMA. Но на самом деле это невозможно, поскольку многоуровневые серверы Skylake еще не выпущены. – Mysticial

+0

Спасибо, обновлено. – Johan

+0

Да, это действительно странно, что на SKL VPSLLVQ более эффективен, чем обычный VPSLLQ (который берет подсчет сдвига только от нижнего элемента). Похоже, что VPSLLQ SKL использует перетасовку порта5, чтобы транслировать счетчик сдвига на каждый элемент вектора, а затем передает его в исполнительные блоки VPSLLVQ. В BDW и более ранних версиях VPSLLQ также принимает порт5 uop, но VPSLLVQ еще медленнее. Во всяком случае, для немедленных смещений (что, вероятно, часто после инкрустации), 'VPSLLQ v, v, i' определенно является наиболее эффективным способом. –