«Одна инструкция для нескольких данных» (SIMD) относится к типу параллельной обработки данных, при котором ЦП или процессор одновременно выполняет несколько идентичных операций с несколькими данными. Этот метод часто используется, когда необходимо эффективно обработать очень большие и похожие объемы данных.

Чтобы представить это более подробно, мы можем рассмотреть пример сложения векторов. Обычно мы просматриваем каждый элемент векторов по отдельности и добавляем их, чтобы получить результирующий вектор.

В примере кода реализация будет выглядеть так:

int main(){
  float a[4] = {1.0f, 2.0f, 3.0f, 4.0f};
  float b[4] = {5.0f, 6.0f, 7.0f, 8.0f};
  float c[4];

  for (int i = 0; i < 4; i++) {
    c[i] = a[i] + b[i];
  }

  return 0;
}

Хорошо видно, что оператор всегда один и тот же (одна инструкция). Однако данные изменяются при каждом проходе цикла (несколько данных).

SIMD позволяет выполнять одно и то же сложение всего за один шаг вместо четырех, путем копирования массивов в SIMD-регистры. Эти регистры обычно имеют размер 128, 256 или 512 бит. В этом примере используется 128-битный SIMD-регистр, поскольку в нем есть место ровно для 4 значений с плавающей запятой.

#include <xmmintrin.h>

int main() {
  float a[4] = {1.0f, 2.0f, 3.0f, 4.0f};
  float b[4] = {5.0f, 6.0f, 7.0f, 8.0f};
  float c[4];

  __m128 vecA = _mm_load_ps(a);
  __m128 vecB = _mm_load_ps(b);
  __m128 vecC = _mm_add_ps(vecA, vecB);
  _mm_store_ps(c, vecC);

  return 0;
}

Нужно ли мне настраивать мой код, чтобы использовать преимущества SIMD?

Да и нет. Современные компиляторы способны идентифицировать такие конструкции и автоматически преобразовывать их в SIMD-код (автоматическая векторизация). Даже если это возможно в теории, компиляторы не распознают каждое место, особенно в сложных местах кода. Поэтому рекомендуется писать их явно с помощью встроенных функций компилятора C.

Аппаратная реализация

SIMD реализуется в современных ЦП и процессорах с помощью специальных схем, называемых модулями SIMD или векторными модулями. Эти схемы специально разработаны для параллельной обработки данных и могут одновременно обрабатывать большое количество данных.

В процессорах Intel и AMD SIMD реализуется с помощью SSE (расширения потоковой передачи SIMD) и AVX (расширенные векторные расширения). Технология SSE поддерживает операции SIMD со 128-битными блоками данных, а AVX поддерживает до 256-битных блоков данных. AVX-512 расширяет технологию до 512-битных блоков данных.

В процессорах Arm функции SIMD добавляются набором инструкций Neon. Они имеют ширину 128 бит.

Примечания

Важно учитывать, что копирование данных между обычным и SIMD-регистром вызывает дополнительные накладные расходы, которые могут повлиять на время выполнения программы. Поэтому копирование следует выполнять как можно реже, чтобы избежать негативного влияния на общее время выполнения.

Кроме того, использование встроенных функций компилятора C ограничивает целевые платформы, поскольку не все процессоры имеют SIMD-регистры. В таких случаях может потребоваться реализация альтернативной версии кода без SIMD для поддержки процессоров без этих регистров.

Первоначально опубликовано на https://csinsight.de 29 апреля 2023 г.