Является ли ваша нехватка узких мест, пропускная способность или объединенных доменов? Если это латентность, то сохранение/перезагрузка ужасно, потому что магазин-переадресация ларька из узких магазинов в широкую нагрузку.
Для пропускной способности и совместимости с доменами, это не ужасно: всего 5 подключенных доменов uops, узкое место на порту магазина. Если окружающий код в основном ALU uops, его стоит рассмотреть.
Для потребительной случае вы предлагаете:
потратив много инструкций/микрооперации на перемещение данных между целыми и векторными регуляров, как правило, плохая идея. PMULUDQ дает вам эквивалент 32-битной мульхи, но вы правы, что 64-битные умножения недоступны непосредственно в AVX2. (У них есть AVX512).
Вы можете использовать 64-разрядный векторный умножитель, используя обычные методы расширенной точности с PMULUDQ. Мой ответ на Fastest way to multiply an array of int64_t? обнаружил, что векторизация 64 x 64 => 64b множителей стоила этого с векторами AVX2 256b, но не с векторами 128b. Но это было с данными в памяти, а не с данными, начинающимися и заканчивающимися векторными правилами.
В этом случае может стоит строить 64x64 => 12 полного умножения из множественных 32х32 => 64-разрядных векторный размножается, но это может занять так много инструкций, что это просто не стоит. Если вам нужны результаты в верхней половине, то лучше всего распаковать скаляр (или сделать все скалярное сканирование).
Целочисленный XOR чрезвычайно дешев, с отличным ILP (латентность = 1, пропускная способность = 4 за часы). Это определенно не стоит переносить ваши данные в векторные regs только на XOR, если у вас нет ничего, что могло бы быть удобным для вектора. Смотрите ссылки x86tag wiki.
Вероятно, лучший способ для латентности является:
vmovq xmm0, r8
vmovq xmm1, r10 # 1uop for p5 (SKL), 1c latency
vpinsrq xmm0, r9, 1 # 2uops for p5 (SKL), 3c latency
vpinsrq xmm1, r11, 1
vinserti128 ymm0, ymm0, ymm1, 1 # 1uop for p5 (SKL), 3c latency
Итого: 7 микрооперации для p5, с достаточно НРП, чтобы запустить их почти все обратно к спине. Поскольку предположительно r8 будет готов к циклу или двум раньше, чем r10, в любом случае, вы не потеряете много.
Также стоит задуматься: что бы вы ни делали, чтобы произвести r8..r11, сделать это с вектором целочисленных инструкций, так что ваши данные уже в XMM регуляров. Затем вам все равно нужно перетасовать их вместе, с 2x PUNPCKLQDQ и VINSERTI128.
Спасибо за подробный ответ еще раз. «xor», вероятно, плохой пример. Дело в том, что все, кроме 'mulx', можно сделать с помощью AVX2. Тем не менее, этого недостаточно, чтобы оправдать стоимость загрузки/хранения. Кроме того, загрузка с YMM на r/64 потребует нескольких перетасовки/перестановки или с помощью 'pextrq' и т. Д. Хотя некоторые задержки могут быть скрыты за счет того, что несколько блоков (YMM) обрабатываются в одной итерации цикла. Я думаю, мне просто нужно поэкспериментировать и узнать для себя. –
@ YanZhou: YMM-> integer с store/reload значительно ниже латентности, чем в другом направлении, потому что функция store-forwarding работает из выровненного широкого магазина, чтобы узкие нагрузки, которые полностью перекрывают его. Кроме того, нагрузки имеют в два раза большую пропускную способность магазинов. Возможно, что извлечение скаляра для чего-то может стоить того, если будет достаточно векторной работы. –
@YanZhou: О, я просто вспомнил, что создание 64-битного вектора, умножающегося на 32-битное векторное умножение, может быть фактически более эффективным, чем переход к скаляру и обратно. См. [Мой ответ на этот вопрос] (http://stackoverflow.com/questions/37296289/fastest-way-to-multiply-an-array-of-int64-t) для эффективного 64 x 64 => 64-битного векторного умножения , Если вам нужны результаты в верхней половине для 64 x 64 => 128 бит векторного умножения, потребуются дополнительные инструкции. (Я забыл, насколько больше работы, может быть, слишком много.) –