Параллельное программирование с использованием архитектуры Haswell
Я хочу узнать о параллельном программировании с использованием микроархитектуры Intel Haswell CPU. Об использовании SIMD: SSE4.2, AVX2 в asm/C/C++/(любые другие языки)? Можете ли вы порекомендовать книги, учебные пособия, интернет-ресурсы, курсы?
Спасибо!
1 ответ
Для меня это звучит так, будто вам нужно больше узнать о параллельном программировании на процессоре. Я начал изучать это около 10 месяцев назад, прежде чем когда-либо использовал SSE, OpenMP или встроенные функции, поэтому позвольте мне кратко изложить некоторые важные концепции, которые я изучил, и некоторые полезные ресурсы.
Существует несколько технологий параллельных вычислений, которые можно использовать: MIMD, SIMD, параллелизм на уровне команд, многоуровневые кадры и FMA. С Haswell есть также вычисления на IGP.
Я рекомендую выбрать тему, такую как умножение матриц или множество Мандельброта. Они оба могут извлечь выгоду из всех этих технологий.
MIMD
Под MIMD я имею в виду вычисления с использованием нескольких физических ядер. Я рекомендую OpenMP для этого. Просмотрите этот учебник http://bisqwit.iki.fi/story/howto/openmp/, а затем используйте его в качестве ссылки https://computing.llnl.gov/tutorials/openMP/. Двумя наиболее распространенными проблемами, связанными с использованием MIMD, являются условия гонки и ложное разделение. Следуйте OpenMP на ТАК регулярно.
SIMD
Многие компиляторы могут выполнять авто-векторизацию, так что я бы посмотрел на это. Авто-векторизация MSVC довольно примитивна, но GCC действительно хороша.
Учитесь внутренностям. Лучший ресурс, чтобы узнать, что делает встроенная функция, это http://software.intel.com/sites/landingpage/IntrinsicsGuide/
Еще один замечательный ресурс - векторный класс Агнера Фога. На 95% вопросов о SO по SSE/AVX можно ответить, посмотрев на исходный код векторного класса. Кроме того, вы можете использовать векторный класс для большинства SIMD и при этом получать полную скорость и пропускать встроенные функции.
Многие люди используют SIMD неэффективно. Прочитайте о массиве структур (AOS) и структуре массивов (SOA) и массиве структур массивов (AOSOA). Также обратите внимание на процесс извлечения полосы Intel. Матричный продукт вычислений намного медленнее с SSE, чем с прямым алгоритмом
См . Кандидатскую диссертацию Инго Уолда об интересном способе внедрения SIMD в трассировке лучей. Я использовал эту же идею для набора Мандельброта для расчета 4(8) пикселей одновременно с использованием SSE(AVX).
Также прочитайте эту статью Wald http://www.cdl.uni-saarland.de/papers/leissa_vecimp_tr.pdf "Расширение C-подобного языка для портативного программирования SIMD", чтобы лучше понять, как использовать SIMD.
FMA
FMA3 является новым со времен Haswell. Это настолько ново, что о SO пока еще мало что говорят. Но этот ответ (на мой вопрос) хорош. Как использовать инструкции Fused Multiply-Add (FMA) с SSE/AVX. FMA3 удваивает пик FLOPS, поэтому потенциально умножение матриц в Haswell вдвое быстрее по сравнению с Ivy Bridge.
Согласно этому ответу, самый важный аспект FMA заключается не в том, что умножение и сложение составляют одна команда вместо двух, а в "(практически) бесконечной точности промежуточного результата". Например, для реализации двойного двойного умножения без FMA требуется 6 умножений и несколько сложений, тогда как с FMA это только две операции.
Параллелизм уровня обучения
У Haswell есть 8 портов, на которые он может отправлять μ-операции (хотя не каждый порт может принимать одну и ту же операционную систему; см. Этот обзор AnandTech). Это означает, что Haswell может выполнять, например, две 256-битные загрузки, одно 256-битное хранилище, две 256-битные операции FMA, одно скалярное сложение и скачок условия в одно и то же время (шесть µ-операций на тактовый цикл).
По большей части вам не нужно беспокоиться об этом, так как это делает процессор. Однако в некоторых случаях ваш код может ограничивать потенциальный параллелизм на уровне команд. Наиболее распространенным является циклическая зависимость. Следующий код имеет циклическую зависимость
for(int i=0; i<n; i++) {
sum += x(i)*y(i);
}
Способ исправить это - развернуть цикл и выполнить частичные суммы.
for(int i=0; i<n; i+=2) {
sum1 += x(i)*y(i);
sum2 += x(i+1)*y(i+1);
}
sum = sum1 + sum2;
Многоуровневые кеши:
Haswell имеет до четырех уровней кэшей. Написание кода для оптимального использования кеша, на мой взгляд, является самой сложной задачей. Это тема, с которой я до сих пор борюсь больше всего и чувствую себя наиболее невежественной, но во многих случаях улучшение использования кэша дает лучшую производительность, чем любая другая технология. У меня нет много рекомендаций для этого.
Вам нужно узнать о наборах и строках кэша (и критических шагах) и о системах NUMA о страницах. Чтобы узнать немного о наборах и критическом шаге, см. http://www.agner.org/optimize/optimizing_cpp.pdf Агнера Фога, и это Почему транспонирование матрицы 512x512 намного медленнее, чем транспонирование матрицы 513x513?
Еще одна очень полезная тема для кеша - это циклическая блокировка или тайлинг Смотрите мой ответ (тот, который набрал наибольшее количество голосов) в разделе Каков самый быстрый способ транспонирования матрицы в C++? для примера.
Вычисления на IGP (с Iris Pro).
Все потребительские процессоры Haswell (Haswell-E еще не выпущены) имеют IGP. IGP использует не менее 30% кремния и более 50%. Этого хватит как минимум еще на 2 ядра x86. Это потраченный впустую вычислительный потенциал для большинства программистов. Единственный способ запрограммировать IGP - это OpenCL. У Intel нет драйверов OpenCL Iris Pro для Linux, так что вы можете работать только с Windows (я не уверен, насколько хорошо это реализовано Apple). Программирование оборудования Intel IGP (например, Iris Pro 5200) без OpenCL.
Одним из преимуществ Iris Pro по сравнению с Nvidia и AMD является то, что двойная с плавающей запятой - это всего лишь одна четверть скорости одиночной с плавающей запятой с Iris Pro (однако fp64 включается только в Direct Compute, а не в OpenCL). NVIDIA и AMD (в последнее время) настолько сильно калечат двойную с плавающей запятой, что делает GPGPU вычисления с двойной плавающей запятой не очень эффективными на своих потребительских картах.