Анализ OpenMP регионов с Intel® VTune™ Amplifier XE

OpenMP* — довольно популярная модель параллельного программирования, особенно для высокопроизводительных вычислений. Но чтобы этой высокой производительности достичь, OpenMP конструкции частенько приходится «настраивать». И здесь не обойтись без хорошего профилировщика. Большинство профилировщиков выдают данные о производительности, ассоциированные с функциями или циклами, но не дают картины по конкретным OpenMP регионам. В результате программист теряет контекст. А без OpenMP-контекста диагностика дисбалансов или накладных расходов становится весьма затруднительной.Intel® VTune™ Amplifier XE умеет профилировать OpenMP регионы. Свежая версия 2015 Update 2 делает анализ гораздо более простым и понятным, благодаря представлению данных в «OpenMP терминах». Инструмент показывает время параллельных и последовательных регионов, разницу между фактическим и идеализированным временем исполнения региона, разбивку по параллельным циклам и загрузку ЦПУ по каждому региону в отдельности.

Пользователь может легче понять, куда вкладывать усилия в первую очередь, благодаря метрике «potential gain». Классификация накладных расходов помогает определить причину неэффективности — например, ожидание из-за дисбаланса нагрузки или на «замке» из-за синхронизации.

Статья описывает некоторые типы OpenMP проблем, определяемых VTune Amplifier, как их нужно понимать и устранять.

64dccaf23be147c6aa68b92e35dbdf48.png

Хорошая новость: со свежими версиями VTune Amplifier и Intel® Compiler почти ничего настраивать не надо. Только выставить одну переменную окружения (пример для Linux): export KMP_FORKJOIN_FRAMES_MODE=3 После этого просто запустите любой тип анализа вашего OpenMP приложения, например, Advanced Hotspots. В этом посте использованы следующие версии продуктов: Выставление переменной нужно временно, часть функционала до сих пор экспериментальна. В будущих обновлениях не нужно будет делать и этого. Для начала, рекомендуется взглянуть на гистограмму CPU utilization на панели Summary. Она содержит время работы всей программы (elapsed time), разбитое по уровням загрузки процессорных ядер. Сюда входит только полезное время, т.е. сжигание циклов во время активного ожидания (spinning) не учитывается.abd6602e58e6403eb1216aa27ac6e116.png

В идеале в параллельном приложении большая часть времени должна приходиться на «зелёную» зону, когда большая часть ядер работает одновременно. На картинке выше показан результат теста с сопроцессора Intel® Xeon Phi™. Большая часть из 224 аппаратных потоков простаивает, чему может быть две основные причины:1. Большая последовательная часть — если параллелизма нет изначально.2. Низкая эффективность параллельных регионов — код параллельный, но какие-то узкие места ограничивают масштабируемость.Теперь смотрим на секцию «OpenMP Analysis» на панели Summary:

784eb21255bc4fcd9168f7564019fb45.png

Время исполнения (elapsed time) разбито на последовательное (Serial Time) и параллельное (Parallel Region Time). Если последовательное время большое, ищите пути сокращения последовательной части. Либо распараллеливать, там, где этого ещё не сделано, либо заниматься другой оптимизацией, например микроархитектурной, если алгоритм никак не параллелится. Чем больше на машине аппаратных потоков, тем сильнее негативный эффект от последовательных участков. В нашем тесте 93.4% времени исполнения проведено в последовательном коде, что есть основной ограничивающий фактор (закон Амдала).

Если мы хотим глубже исследовать последовательных участок, переключаемся на вкладку Bottom-up, выбираем группировку по »/OpenMP Region/Thread/Function…» и фильтруем по мастер-потоку (Master Thread) в строке »[Serial — outside any region]».

677f7a1d8b92405c88b55cc9563c815f.png

Здесь в Bottom-up видно, что последовательный участок занимает 21.571 секунд, большую часть всего времени исполнения. Однако процессорного времени (суммарно по всем потокам) гораздо больше в параллельных участках — OpenMP регион на строке 179 тратит 164 секунды, и это только один регион. Фильтроваться по мастер-потоку нужно потому, что может быть много рабочих потоков OpenMP, которые просто ждут, пока мастер-поток исполняет последовательный код. И ждут они активно, прожигая циклы процессора — это время надо исключить, если хотим сконцентрироваться на собственно последовательном коде. В нашем тесте это только 20.713 секунды процессорного времени. И на многоядерном сопроцессоре Intel Xeon Phi это убивает производительность — закон Амдала в действии.

Общая эффективность параллельных регионов может быть оценена на «OpenMP Region CPU Usage Histogram» на вкладке Summary. Это то же самое, что CPU usage histogram, но в приложении к конкретному параллельному региону. Ниже показана загрузка ЦПУ из того же теста на Intel Xeon Phi, но для OpenMP региона на строке 153. Здесь загрузка куда лучше — большая часть близка к идеальной, остальная смещена вправо от нуля. Т.е. на этом параллельном регионе ядер работает больше, чем в среднем по программе, что ожидаемо.

fae75a89e68f4b52825f585ffa7fbada.png

Разберём другой пример. Мы проанализировали NAS Parallel Benchmarks (NPB) на предмет возможных проблем в исполнении OpenMP регионов.Тестовая конфигурация:

CPU: Intel® Xeon® processor E5–2697 v2 @ 2.70GHz, 24 cores/48 threads. OS: RHEL 7.0×64 Compiler: Intel® Parallel Studio XE Composer Edition 2015 update 2 Workload: NPB 3.3.1, «CG — Conjugate Gradient, irregular memory access and communication» module, class B. Количество OpenMP потоков выставлено в 24, что соответствует количеству физических ядер. Гистограмма CPU utilization показывает неплохую загрузку, но всё-ещё не идеальную — значительное время проведено лишь с 2–6 одновременно занятыми ядрами. Программа параллельна, но не всегда использует все 24 ядра: ffa9cfaf4b994562a180f3ecae2555c4.png

Последовательное время в NPB ничтожно и не является проблемой. Но обратите внимание на метрику «Potential Gain» (подсвечено розовым):

fe4267315860443abc9407efb8e89e4c.png

Potential Gain — это разница времени исполнения между реальным замером и идеальным случаем, если бы нагрузка на все потоки была идеально сбалансирована и накладные расходы OpenMP рантайма были бы нулевыми. Т.е. Potential Gain — потенциальный выигрыш от оптимизации, это максимальное время, которое вы можете выиграть, улучшая параллельное исполнение. Эта метрика может быть важнее, чем время исполнения и процессорное время, т.к. она фокусирует вас не просто на самый затратный регион, а на регион, в котором вы скорее всего получите наилучший результат от оптимизации (что вы в общем-то обычно и ищите, иначе зачем профилировать?).

На картинке выше Potential Gain, подсвеченная розовым, говорит, что оптимизация всех параллельных регионов до идеального состояния может сократить время исполнения всей программы на 3.975с или 34.9% — есть за что побороться, хоть до идеала конечно не дотянем.

До сих пор мы оперировали метриками всего приложения — давайте пойдём глубже, на уровень параллельных регионов. Summary содержит топ 5 OpenMP регионов по potential gain. В нашем случае весь потенциальный выигрыш (3.958с) сконцентрирован в одном регионе на строке 514. Это хорошо для нас — проблема сужена до одного единственного региона.

Когда мы сфокусировались на конкретном регионе, щёлкаем по нему в Summary и переходим по гиперссылке в Bottom-up, который сам группируется по OpenMP регионам и выделяет тот, что нужен нам: 64dccaf23be147c6aa68b92e35dbdf48.png

Таблица в Bottom-up содержит разную статистику о параллельных регионах: время исполнения региона (elapsed time), potential gain, количество рабочих потоков OpenMP, количество вхождений в регион (instance count). Время ЦП (CPU time) разбито на effective time (код самого приложения), spin time (время активного ожидания), и overhead time (накладные расходы). Время активного ожидания у нас значительно и подсвечено розовым — 92.159с. Прежде чем копать глубже, бросим беглый взгляд на исходный код. Наш параллельный регион на строке 514 состоит из множества параллельных циклов »!$omp do». Это плохая новость, потому что наши метрики для всего региона, и неясно, к каким циклам их относить:

ffb8d6a0cfaf4886becc1e24ad48021d.png

И снова хорошая новость — VTune Amplifier умеет разбивать данные не только по параллельным регионам, он и по OpenMP барьерам. Как известно, все конструкции »#omp for» или »!$omp do» имеют барьеры синхронизации, если не указана опция «nowait». Т.к. VTune Amplifier распознаёт эти барьеры, мы сможем увидеть время исполнения и ЦП для каждого барьера, т.е. и для каждого параллельного цикла внутри региона. Здесь потребуется создать кастомную группировку по »/OpenMP Region/ OpenMP Barrier Type/ OpenMP Barrier/…» — см. маленькую кнопку справа в строке группировок.

После группировки по барьерам, всё становится яснее. Во-первых, большая часть времени и potential gain происходит из Loop barrier на строке 572 (выделено на скриншоте, строка не поместилась):

e1e8c09048e34d6b8a830e01abf14703.png

Во-вторых, разворачиваем колонку Spin Time по категориям, и видим, что всё активное ожидание происходит из-за дисбаланса (Imbalance подсвечено розовым). В-третьих, колонка «OpenMP Loop Schedule Type» говорит о том, что наш параллельный цикл имеет статическую диспетчеризацию.

Цикл на 572-й строке содержит только »!$omp do», безо всяких «schedule» опций. Поэтому тип диспетчеризации по умолчанию статический, о чём и сказал профилировщик.6b3da322959c4e68b30f48e4020148a2.png

Т.к. цикл страдает от дисбаланса, логично попробовать динамическую диспетчеризацию — пусть нагрузка распределятся автоматически:

64475e7bc09246f08fa3cfd4b4ed3548.png

После перехода к динамической диспетчеризации производительность цикла стала ещё хуже. Время исполнения увеличилось с 10.445с до 11.102с: 7df40b99b5194631a8695f9666c22716.png

Картина в таблице поменялась — дисбаланс и spin time пропали, значит его мы всё-таки исправили, уже неплохо. Но теперь подсвечена новая проблема — 74.99с времени ЦП уходит в накладные расходы на диспетчеризацию (scheduling overhead). OpenMP рантайм слишком много обрабатывает что-то внутри.

Колонка «OpenMP Loop Chunk» поменялась с изначальных 3125 на 1. Значит каждая итерация диспетчеризуется индивидуально, при том, что количество вычислений за одну итерацию совсем невелико — см. код выше. Кусочки работы для потоков OpenMP очень малы, и распределять их по потокам надо очень часто — рантайм работает слишком интенсивно, сам тратит много процессорного времени. Параллелизм выходит слишком мелко гранулярным.

Chunk size или Grain size равен единице по умолчанию для динамической диспетчеризации. Проверим предположение о слишком мелкой гранулярности — увеличим его до 20:

65e839863c6b4f14b76a7382108e91fa.png

Смотрим в результаты профилировки. Дисбаланс и накладные расходы теперь тратят всего около 1 секунды времени ЦП. Барьер цикла на строке 572 опустился в списке горячих циклов, потому что его potential gain уменьшился до 0.077с с начальных 3.133с. Время исполнения цикла упало с 10.445с до 8.928с. Время ЦП для всего параллельного региона сократилось с ~250с до ~213с. Так что мы получили некоторый выигрыш в производительности, хоть и меньше потенциальных 3х секунд:

719594b7afa5483bbb99f0b87d51f89a.png

Анализ производительности OpenMP приложений с VTune Amplifier XE стал более естественным — инструмент оперирует терминами и понятиями OpenMP, а не аппаратными и системными метриками. Можно анализировать узкие места от общего к частному, начиная с оценки последовательной части и загрузки процессора для всего приложения, проверить загрузку ЦП для отдельного параллельного региона. Метрика potential gain фокусирует разработчика на наиболее интересных возможностях для оптимизации. Разбивка данных по барьерам позволяет анализировать регионы с множеством параллельных циклов.OpenMP-статистика в Bottom-up помогает определить причину неэффективности. Тип диспетчеризации, chunk size, количество потоков и вызовов региона, категоризация времени активного ожидания и накладных расходов — всё это поможет понять, что ограничивает производительность: дисбаланс, мелкая гранулярность, объекты синхронизации или что-то ещё.Ссылки: — OpenMP анализ — совместная работа библиотеки Intel OpenMP и VTune Amplifier XE, оба они доступны в пакете Intel® Parallel Studio XE 2015 Professional edition.— Официальный сайт и дополнительные материалы по VTune Amplifier XE.— Более полная и детальная статья про OpenMP проблемы, определяемые VTune Amplifier (англ.)

© Habrahabr.ru