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

GPGPU (универсальный графический процессор) - это вариант для этого. GPGPU предоставляют нам массивные параллельные ядра для обработки нескольких данных одновременно, распределения данных по всем ядрам для выполнения определенной инструкции. Благодаря этой возможности GPGPU вычисляют в 10 раз быстрее, чем скалярный ЦП (возможно, больше, в зависимости от количества ядер и размера DRAM графического процессора). Но очевидно, что они довольно дорогие.

Потребительские / игровые графические процессоры довольно популярны и могут стать еще одним вариантом решения проблемы усовершенствованного ускорителя вычислений. Однако они не предназначены для непрерывного выполнения вычислений с высокой нагрузкой, как это необходимо, когда мы обучаем модель машинного обучения, делаем вывод о приложении ИИ, секвенируем ДНК или запускаем компьютерное моделирование. Следовательно, его долговечность может быть серьезной проблемой.

У одного из ведущих производителей графических процессоров, Nvidia, есть проект, единственной целью которого является расширение возможностей видеокарты корпоративного уровня в высокоуровневых вычислениях, а также оценка ее долговечности. Их графические процессоры названы в честь одного из величайших ученых 90-х годов Теслы. Последняя версия Tesla, Volta V100, имеет выдающуюся производительность с одинарной точностью - 15,7 терафлопс [1].

В отличие от игровых графических процессоров, графические процессоры Nvidia Enterprise, такие как серия Tesla, не являются OEM (производителем оригинального оборудования). Другими словами, они дороже игровых графических процессоров (даже по сравнению с графическими процессорами, выпускаемыми под разными торговыми марками, такими как ASUS, Leadtek, Digital Alliance и т. Д.). В заключение, корпоративные графические процессоры могут стать промежуточным звеном между GPGPU и игровыми графическими процессорами с точки зрения их цены, надежности и вычислительной мощности.

но что, если я не могу позволить себе графический процессор, но отчаянно нуждаюсь в быстрых вычислениях?

Как вы уже могли заметить, графическая карта для современных вычислений (Google TPU, Intel Xeon Phi) и корпоративные графические процессоры могут быть немного дорогими. Если ваши огромные вычислительные нагрузки не имеют потенциала продаж или ваш проект не подкреплен большой кучей денег на исследования, они могут быть неправильным выбором.

Но не беспокойтесь, для тех из вас, кто владеет ноутбуком / ноутбуком даже без графического процессора, но думает об увеличении вычислительной мощности примерно в 2–3 раза, на самом деле есть скрытое сокровище, которое ждет своего выхода, которое может достичь таких вещей.

Набор инструкций расширения был представлен для Intel, AMD, ARM и некоторых других процессоров. Этот новый набор инструкций создается вместе с их набором инструкций по умолчанию и предназначен для ускорения выполнения текущей арифметической операции.

Ориентируясь в основном на оптимизацию данных, этот набор команд использует более широкие реестры для упаковки большего количества данных в один пакет для дальнейшего выполнения. Таким образом достигается параллелизм данных (и более высокая пропускная способность). Этот набор команд расширения смоделирован по образцу SIMD (Single Instruction Multiple Data), который будет объяснен далее в следующем разделе.

SIMD (одна инструкция, несколько данных)

SIMD (Single Instruction Multiple Data) - это тип операции, который обрабатывает несколько данных одновременно на одном арифметическом устройстве. SIMD - это один из классов в Таксономии Флинна. Полный список таксономии Флинна выглядит следующим образом:

  • SISD (одиночные инструкции одиночных данных)
  • SIMD (одна инструкция, несколько данных)
  • MISD (несколько инструкций, отдельные данные)
  • MIMD (несколько инструкций, несколько данных)

SIMD в основном используется для векторных операций, таких как сложение, умножение, скалярное произведение и т. Д.

С помощью SIMD мы можем сериализовать / упаковать векторные элементы сразу, вычислить их с помощью определенных инструкций и получить каждый результат одновременно.

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

Ниже приведен список наборов инструкций SIMD, доступных для каждой марки ЦП:

SSE (потоковые расширения SIMD)

До появления SSE был пройден долгий путь эволюции технологии набора инструкций. Начнем с набора инструкций MMX, представленного Intel Pentium в 1997 году, это дополнительный набор инструкций наряду с его набором инструкций IA-32 x86. Реестры MMX (mm0 - mm7) имеют ширину 64 бита и могут содержать целые числа длиной 64 бита или несколько меньших целых чисел.

SSE впервые появился в Intel Pentium 4 и призван заменить текущий набор инструкций MMX. SSE с использованием реестров XMM (xmm0 - xmm7) шириной 128 бит. Следовательно, по сравнению с MMX, он может хранить вдвое больше данных, а значит, вдвое большую пропускную способность.

Чтобы использовать эти наборы инструкций SIMD, существует внутренняя библиотека C, которая поддерживает новый тип данных и наборы функций C, подготовленные Intel. В реестры этого расширения вводятся несколько типов данных, которые будут использоваться для выполнения операции SIMD, перечисленные ниже:

  • __m64
  • __m128
  • __m128i
  • __m128d

__m64 используется наборами инструкций MMX. Он может упаковывать одно 64-битное значение, два из 32-битного значения, четыре из 16-битного значения или восемь из 8-битного значения.

__m128 используется наборами инструкций SSE. он может содержать четыре из 32-битных значений

__m128i используется наборами инструкций SSE, в частности, для целочисленных значений (16 x 8-битное значение) или (8 x 16-битное значение) или (4 x 32-битное значение) или (2 x 64-битное значение)

__m128d используется наборами инструкций SSE специально для значения с плавающей запятой (4 x 32-битное значение) или (2 x 64-битное значение)

Большинство встроенных функций / синтаксиса SSE имеют определенное соглашение об именах в соответствии с шаблоном ниже:

_mm_[intrinsic_operation]_[suffix]

e.g

__m128 dst _mm_add_ps(__m128 a, __m128 b) выполняет сложение 128-битного значения a и 128-битного значения b и сохраняет результат в 128-битном dst.

add - это внутренняя операция, которая, как следует из названия, выполняет сложение.

ps - суффикс, обозначающий упакованную с плавающей запятой. «p» обозначает управляемый упакованный элемент, а «s» обозначает элемент данных с плавающей запятой одинарной точности (32 бита).

Внутренние операции охватывают многие общие арифметические операции (сложение, умножение, деление и т. Д.), Логические операции, операции сравнения, а также операции преобразования. Чтобы получить доступ к встроенной библиотеке Intel C, вам необходимо включить файл заголовка C

#include "xmmintrin.h" // for SSE
#include "emmintrin.h" // for SSE2
#include "nmmintrin.h" // for SSE4.2

Более подробный список встроенных функций встроенной библиотеки Intel вы можете найти по ссылке ниже:

«Https://software.intel.com/sites/landingpage/IntrinsicsGuide Ана/

Давайте попробуем простое сложение векторов с использованием встроенной функции SSE C (объяснение в комментарии)

Для компиляции внутреннего кода SSE C с использованием GCC требуются дополнительные аргументы -msse

gcc --std=c99 -msse -o sse_add sse_add.c

Давайте сравним время выполнения SSE и простого сложения векторов в C.

Простой код сложения векторов на C без встроенного будет следующим:

gcc --std=c99 -o vec_add vec_add.c

Выполнение простого сложения векторов с длиной вектора (2048 * 2048) в C без встроенной функции занимает 12,03 мс, а выполнение в SSE C внутренней занимает 9,44 мс, что составляет ~ 27 % быстрее.

Ускорение довольно очевидное и значительное. Немного проанализировав код, чтобы увидеть разницу между выполнением на простом наборе инструкций x86 и расширенным набором инструкций, мы получаем четкий вывод, почему происходит это ускорение.

// SSE intrinsic can reduce iteration with maximum number of packed
int packed = 4;
for(int i=0;i<VECTOR_SIZE / packed;i++){
  __m128 _vec_a = _mm_loadu_ps(a + i * packed);
  __m128 _vec_b = _mm_loadu_ps(b + i * packed);
  __m128 _vec_c = _mm_add_ps(_vec_a, _vec_b);
  memcpy(c + (i*packed), &_vec_c, packed * sizeof(float));
 }

Ключ к увеличению производительности в SIMD - это сокращение количества итераций, вызванное одновременным выполнением нескольких данных, что максимизирует пропускную способность. Следовательно, за один раз можно упаковать больше данных, что означает сокращение количества итераций, что значительно повысит производительность.

Это причина развития технологии SIMD за счет увеличения размера реестра на уровне инструкций для выполнения выполнения.

Вывод

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

Использованная литература)

[1] Технические характеристики NVIDIA Tesla V100, https://www.nvidia.com/en-us/data-center/tesla-v100/