[Перевод] Простая методика оптимизации с использованием Intel System Studio

16d42cb76df247aab8f21499431962f8.jpgВ этой статье мы описываем простую методику оптимизации с использованием Intel Cilk Plus и компилятора Intel C++ на основе результатов анализа производительности, проведенного с помощью Intel VTune Amplifier. Intel System Studio 2015 содержит упомянутые компоненты, использованные для этой статьи.
• Intel VTune Amplifier — интегрированный инструмент для анализа производительности, помогает разработчикам анализировать сложный код и быстро обнаруживать узкие места.
• Компилятор Intel C++ создает оптимизированный код для архитектур IA-32 и Intel 64. Также предоставляются различные возможности, помогающие разработчикам повышать производительность своих программ.
• Intel Cilk Plus — это расширение языка C/C++, входящее в состав компилятора Intel C++, позволяет повышать производительность за счет распараллеливания новых или существующих программ на языке C или C++.

Стратегия


Мы используем один из примеров кода в учебном руководстве по VTune, tachyon_amp_xe, в качестве целевого кода для повышения производительности. Этот пример выводит изображение сложных объектов.

Методика оптимизации производительности, применимая к этому примеру, описана ниже.

  1. Запуск простого анализа узких мест или общего анализа для примера проекта в интегрированной среде разработки, например в Visual Studio* 2013.
  2. Выявление узких мест и других возможностей для оптимизации.
  3. Применение изменений кода к обнаруженным узким местам.
  4. Изучение возможностей оптимизации в компиляторе.
  5. Распараллеливание кода.

Оптимизация


Тестовая среда
ОС: Windows 8.1
Набор инструментов: Intel System Studio для Windows, обновление 3
Среда разработки: Microsoft Visual Studio 2013

Шаг 1. Интерпретация и анализ данных результатов


Запуск общего анализа (если это невозможно, перейдите к простому анализу узких мест) и обнаружение узких мест. Этот пример кода создан как пример для обнаружения узких мест и повышения производительности. Поэтому для работы с ним можно использовать страницу примера the tachyon_amp_ex. После запуска примера в VTune мы получим следующие результаты.

73557dbfdf064f0c9e22f6417e57dd4d.PNG

Видно, что время, затраченное этим приложением, составило 44,834 секунды, и этот базовый уровень производительности мы попытаемся улучшить.
Кроме того, в этом примере приложения функция initialize_2D_buffer расходует больше всего времени — 18,945 секунды, поэтому она занимает верхнюю строку в списке ресурсоемких функций. Мы попробуем оптимизировать именно эту функцию, занимающую больше всего времени.

d236156162e24716b860d0dbd77e5e03.PNG

Гистограмма нагрузки на ЦП, приведенная выше, показывает, что в этом примере не используется параллельная обработка. Поэтому есть возможность использовать многопоточную архитектуру для ускорения обработки ресурсоемких задач.

Шаг 2. Алгоритмический подход для функции initialize_2D_buffer

36b039cc11c74407b31c5cbfd9bd73e7.PNG

Как мы видели, выполнение функции initialize_2D_buffer занимает больше всего времени, причем эта функция задействует наибольшее количество инструкций. Из этого следует, что если где-то и можно добиться повышения производительности, то именно здесь эффект будет наибольшим.

116ef42cbf8344d88b5d71bada33e50f.PNG

Дважды щелкните имя функции. VTune Amplifier откроет файл с исходным кодом именно на той строке, где на работу этой функции затрачивается больше всего времени. Для функции initialize_2D_buffer эта строка служит для инициализации массива памяти с помощью разрозненных расположений в памяти (находящихся не подряд). У этого примера кода уже есть более быстрая альтернатива циклу for.

Ниже приведен фактический код функции initialize_2D_buffer. Первый цикл for заполняет целевой массив непоследовательно, тогда как второй цикл for выполняет эту же задачу последовательно. Можно повысить производительность, задействовав второй цикл for.

acec1ad232894e41a77757b3cf4079c9.PNG

5e8a4c13669d43beaf33ce6fa66ec5c9.PNG

После замены цикла for на второй мы отмечаем некоторый прирост производительности. Рассмотрим новые результаты профилирования в VTune.

По сравнению с прежним результатом общее время выполнения снизилось с 44,834 до 35,742 секунды, то есть примерно в 1,25 раза. Время работы нашей целевой функции сократилось с 18,945 до 11,318 секунды, то есть примерно в 1,67 раза.

Шаг 3. Возможности оптимизации компилятора


Мы зачастую упускаем из вида возможности автоматической оптимизации, доступные в компиляторах. В этом случае мы просто включаем встроенную оптимизацию компилятора Intel C++, указав параметр /O3 при компиляции. Это же можно сделать и с помощью графического пользовательского интерфейса. Чтобы использовать параметр «/O3», нужно сначала установить компилятор Intel C++ в качестве компилятора по умолчанию для данного проекта.

7da006534faa4f1cb5911c07cc37ec5b.PNG

6c88892733964c68a3f2c3b95739ad48.PNG

Одного лишь добавления этого параметра иногда бывает достаточно для существенного повышения производительности. Подробные сведения о параметре оптимизации /O[n] см. здесь. Новые результаты: для выполнения задачи достаточно 24,979 секунды. Прежде нужно было 35,742 секунды, то есть теперь программа работает в 1,43 раза быстрее.

477dac56cf5b4fcea425c72cdf2eb7cd.PNG

Шаг 4. Распараллеливание с помощью Cilk Plus


Само по себе параллельное программирование — достаточно обширная область. Существует множество способов реализации параллельных вычислений в системе, рассчитанных на работу на многоядерных платформах. На этот раз мы используем Intel Cilk Plus — это довольно простое расширение языка, которое очень неплохо работает.

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

На этот раз мы просто будем работать с функцией draw_trace в файле find_hotspots.cpp. Достаточно просто добавить cilk_for для распараллеливания нужной нам задачи: вместо одного потока будет задействовано несколько. После этого наглядно видна работа четырех потоков (код тестировался на двухъядерном процессоре с технологией гипертрединга), одновременно рисующих разные линии.

6ef31a8820d74c6396ed6331320e0ee1.PNG

28404e6680ff4064b46f190c1d68d316.PNG

22b6460b671149a7af7954dc8346f680.PNG

Теперь для завершения работы достаточно 11,656 секунды, что значительно лучше первоначального времени. Рассмотрим результаты VTune.
Общее время работы составляет 13,117 секунды, то есть в 1,9 раза быстрее, чем раньше. Мы видим, что многоядерная архитектура эффективно используется.

128290f858b548cbb4f136434a3b11b2.PNG

Заключение


Время работы всей программы снизилось с 44,834 до 13,117 секунды, то есть в 3,41 раза.
Такого уровня оптимизации удалось достичь всего лишь с помощью простого анализа в VTune, добавления параметра компилятора Intel C++ и применения Cilk Plus.
Компоненты Intel System Studio помогают разработчикам без особых усилий существенно улучшать свои программы.

© Habrahabr.ru