Похоже, что это вопрос об исполнении заказа?Современные x64 имеют несколько исполнительных портов на процессоре, и каждый из них может отправлять новую инструкцию за такт (таким образом, на процессоре Intel SkyLake может параллельно выполняться около 8 операций процессора).Некоторые из этих портов обрабатывают загрузку / хранение памяти, некоторые обрабатывают целочисленную арифметику, а некоторые обрабатывают инструкции SIMD.
Так, например, вы можете иметь возможность отправлять 2 плавающих набора AVX, битовую операцию AVX, 2 загрузки AVX, одно хранилище AVX и пару битов арифметики указателей на регистры общего назначения водин цикл [вам придется ждать завершения операции - задержка].Таким образом, теоретически, до тех пор, пока в коде нет ужасающих цепочек зависимостей, с некоторой осторожностью вы должны поддерживать занятость каждого из этих портов (или, по крайней мере, это основная цель!).
Простое правило 1 : чем больше вы заняты портами исполнения, тем быстрее работает ваш код.Это должно быть само собой разумеющимся.Если вы можете держать 8 портов занятыми, вы делаете в 8 раз больше, чем если бы вы могли держать только 1 занятым.В общем, это в основном , не стоит беспокоиться о (да, всегда есть исключения из правила)
Простое правило 2 : Когдаиспользуются порты выполнения SIMD, ALU не становится внезапно бездействующим [Небольшая ошибка терминологии с вашей стороны: ALU - просто бит процессора, который выполняет арифметику.Вычисление для операций общего назначения выполняется на ALU, но также правильно называть SIMD-модуль ALU.Вы хотели спросить: выключаются ли части ЦП общего назначения при использовании SIMD-блоков?На что нет ответа ...] .Рассмотрим этот оптимизированный метод AVX2 (который не делает ничего интересного!)
#include <immintrin.h>
typedef __m256 float8;
#define mul8f _mm256_mul_ps
void computeThing(float8 a[], float8 b[], float8 c[], int count)
{
for(int i = 0; i < count; ++i)
{
a[i] = mul8f(a[i], b[i]);
b[i] = mul8f(b[i], c[i]);
}
}
Поскольку нет никаких зависимостей между a, b и c (о которых я действительно должен быть явным, указав __restrict), тогда два SIMDИнструкции умножения могут быть отправлены за один тактовый цикл (поскольку есть два исполнительных порта, которые могут обрабатывать умножение с плавающей запятой) .
АЛУ общего назначения здесь не отключается внезапно - регистры и инструкции общего назначения все еще используются!1. вычислить адреса памяти (для: a [i], b [i], c [i], d [i]) 2. загрузить / сохранить в этих ячейках памяти 3. увеличить счетчик циклов 4. проверитьесли счет был достигнут?
Просто так получается, что мы также используем блоки SIMD для нескольких умножений ...
Простое правило 3 : Для операций с плавающей запятой использование 'float' или '__m256' практически не имеет значения.Те же аппаратные средства ЦП, которые использовались для вычисления типов float или float8, абсолютно одинаковы.В кодировке машинного кода есть просто пара битов, которые определяют выбор между float / __m128 / __m256.
т.е. https://godbolt.org/z/xTcLrf