Введение в оптимизацию приложений с использованием компиляторов Intel

Использование инструментов Intel® для оптимизации программ

Разбить на страницы
Показывать лекцию целиком

Презентацию к лекции Вы можете скачать здесь.

Основные характеристики приложения, влияющие на его производительность

  • Эффективность вычислений
  • Эффективность работы с памятью
  • Правильное предсказание переходов
  • Эффективность использования векторных инструкций
  • Эффективность параллелизации
  • Уровень инструкционного параллелилизма
  • Под эффективностью вычислений подразумевается "не делать лишней работы", что достигается эффективным анализом потока данных. Избегать повторных сложных вычислений, протягивать и свертывать константы, удалять "мертвый код".

    Место и роль компилятора

    Компилятор — транслятор, который осуществляет перевод всей исходной программы в эквивалентную ей результирующую программу на языке машинных команд или на языке ассемблера.

    Основная задача оптимизирующего компилятора – получение кода максимально эффективного для используемого вычислительного комплекса.

    С точки зрения разработчика программа должна быть:

  • Легко читаемой и модифицируемой
  • Легко отлаживаемой
  • Быстро исполняемой
  • Разработчику необходима

  • надежная унифицированная среда разработки
  • возможность варьировать уровни отладки и быстродействия
  • возможность получать высокоэффективный код для различных операционных систем и микропроцессорных архитектур.
  • Компилятор должен удовлетворить эти требования.

    В настоящее время от компилятора требуется получение эффективного и высоконадежного кода. С точки зрения разработчика программа должна быть легко читаемой и модифицируемой. Для реализации этого требования в больших проектах необходима модульность, основная функциональность выносится в отдельные утилиты, используется объектно-ориентированное программирование и т.д. Благодаря этому оптимизация программного кода – сложная и комплексная задача. Дополнительный уровень сложности возникает из-за обилия поддерживаемых архитектур и различных расширений. В результате оптимизирующий компилятор сложный программный комплекс, включающий в себя большое разнообразие оптимизирующих техник.

    Оптимизирующий компилятор

    Это программный комплекс, работа которого варьируется в зависимости от требований к результирующему коду.

    Возникают следующие проблемы:

  • Сложность доказательства допустимости тех или иных оптимизаций
  • Сложность расчета выгодности оптимизаций
  • Отсутствие во время компиляции представления о типичных входных данных
  • Для достижения хороших результатов требуется тесное сотрудничество с разработчиком

    Чтобы использовать умело средства компилятора, программист должен:

  • иметь представления о архитектуре, на которой будет использоваться его программа
  • ознакомиться с настройками компилятора
  • ознакомиться с основными техниками улучшения производительности, которые использует компилятор
  • ознакомиться с основными проблемами, вызывающими замедление работы программы
  • знать примерные данные, с которыми будет работать программа
  • уметь пользоваться инструментами для анализа производительности программы.
  • Компиляторы Intel

    С/C++ и Fortran для операционных систем Windows, Linux и Mac OS

    Для Windows компилятор может быть интегрирован в Microsoft Visual Studio

    Главной целью корпорации является высокая производительность компиляторов

    и совместимость с Microsoft Visual Studio на Windows и с gcc на Linux и Mac OS.

    Некоторые полезные опции компилятора

  • /Od (-O0 for Linux) – оптимизации отключены, дебаг-режим.
  • /O2 (-O2 for linux) – оптимизации "по-умолчанию" .
  • /O3 (-O3 for linux) – дополнительные оптимизации.
  • /xO (-xO for Linux) – оптимизация под неинтеловскую архитектуру.
  • /Qipo (-ipo) - межпроцедурная оптимизация.
  • /Qparallel (-parallel) – автопараллелизация.
  • /Qopt-report (-opt-report)
  • /Qopt-report-file
  • /Qopt-report-phase
  • /Qopt-report-help
  • /Qopt-report-routine
  • /Qvec-report [1/2/3]
  • Очень часто, чтобы сделать приложение быстрее, надо просто выбрать правильный компилятор и установить ему правильные опции.

    Дополнительные инструменты оптимизации приложений

    Существуют различные средства поиска неоптимальных участков кода в зависимости от типов решаемых задач и сред исполнения

    Intel VTune™ Amplifier XE Performance Profiler

  • предоставляет информацию о производительности программ
  • подходит как для последовательных, так и для многопоточных приложений
  • версии как для Windows, так и для Linux
  • для Windows может интегрироваться в Visual Studio или работать отдельно с собственным GUI
  • для Linux – только отдельно
  • есть возможность использования из командной строки для удалённого сбора данных или регрессивного тестирования
  • Intel VTune – многофункциональный инструмент, предназначенный для анализа производительности приложения. Он позволяет получить полную информацию о том, как на самом деле работает ваша программа. Бывает, что даже опытный разработчик не всегда может верно оценить, на что тратятся основные вычислительные ресурсы и какие вычисления являются "узким местом", ограничивающим производительность всего приложения.

    Intel VTune™ Amplifier XE Performance Profiler

    Помогает проанализировать особенности алгоритма и определить фрагменты приложения, где оно может использовать доступные ресурсы более рационально.

    Позволяет найти и определить следующее:

  • функции, на вычисление которых тратится основное время (горячие функции)
  • фрагменты кода, которые не используют процессор эффективно
  • области, наиболее сильно нуждающиеся в оптимизации
  • синхронизации, влияющие на производительность приложения
  • фрагменты кода, создающие эффект бутылочного горлышка
  • Intel VTune особенно удобен тем, что для решения большинства своих задач он не требует пересборки программы. VTune – не статический анализатор, он собирает информацию о том, как работает интересующее приложение или вся система в реальных условиях, на реальных данных, "под нагрузкой". Желательно, что бы исследуемая программа была скомпилирована с сохранением отладочной информации. В этом случае можно будет "провалиться" вплоть до исходного кода и получить детальную характеристику каждой строчки исполняемого кода. Какая именно информация будет собрана и насколько она будет подробной, зависит от режима работы Intel VTune и от настроек. Далее мы познакомимся и с возможными режимами работами и с настройками. Отведённое нам время позволит это сделать лишь достаточно поверхностно, в виде ознакомления. Тем не менее основные моменты станут понятными, подробности вы всегда сможете узнать у самого VTune. VTune мощный и, вообще говоря, достаточно сложный инструмент. Точнее, с его помощью можно делать весьма сложные вещи, крайне трудно осуществимые без его помощи. Тем не менее, пользоваться им не сложно. Это инструмент с долгой историей, с набором дочерних и параллельных проектов, которые, в том числе, активно используются и внутри компании. Помимо документации VTune содержит отличный контекстный help, который поможет вам разобраться. Так же есть документация, как я уже сказал, также форумы поддержки и базы знаний Intel, ну и Google спешит на помощь?

    Почему здесь выделена Java? Потому что с подобными языками есть проблема. А именно, исполняемого кода в явном виде не существует. Исполняемый код создаётся Just In Time Compiler’ом прямо в памяти непосредственно во время работы приложения. Более того, со временем код может перекомпилироваться для получения лучшей производительности. Особенность VTune в том, что он умеет работать с такими вещами.

    Шаги по использованию VTune

  • Выбрать объект для анализа
  • Скомпилировать
  • Запустить анализ
  • Интерпретировать результаты
  • Улучшить приложение при помощи алгоритмов оптимизации
  • Сборка приложения

    Для получения более детальной информации посредством VTune рекомендуется компилировать приложения в режиме debug с генерацией символьной информации

    Запуск анализа

    Запуск из графического интерфейса осуществляется интуитивно понятно

    Анализ

  • После выполнения анализа отображается общая информация
  • Список содержит функции, на выполнение которых было затрачено наибольшее количество времени
  • Стек вызовов

    Можно просматривать не только функцию, но и стек вызова

    Просмотр по вычислительным потокам

  • 1 – время
  • 2 – вычислительные потоки
  • 3 – использование процессора
  • Исходный код

  • 1 – код программы, 2 – ассемблерный код,
  • 3 – процессорное время,
  • 4 и 5 – элементы навигации для быстрого перемещения по горячим строкам кода
  • Сравнение результатов

    Для определения выигрыша, вызванного оптимизациями удобно воспользоваться функцией сравнения

    Сравнение результатов

  • 1 – различие во времени между версиями
  • 2 – время до оптимизаций
  • 3 – время после оптимизаций
  • Оценка эффективности использования вычислительных ресурсов

    Аналогичным образом можно произвести другие виды анализа, например, locks and waits

    Осуществление анализа

    После выполнения анализа вам также будет представлена краткая информация, но уже по другому аспекту

    Осуществление анализа

    Также имеется возможность просмотра более детальной информации по стеку вызова и объектам синхронизации

  • 1 – объект,
  • 2 – использование процессора,
  • 3 – количество циклов ожидания
  • Анализ исходного кода

  • 1 – строки кода,
  • 2 – использование процессора,
  • 3 – общее количество циклов ожидания,
  • 4 - навигация
  • Сравнение результатов

  • 1 – различия во времени ожидания,
  • 2 – время ожидания до оптимизаций,
  • 3 – время ожидания после оптимизаций,
  • 4 – разница в количестве циклов ожидания,
  • 5 и 6 – количество циклов ожидания
  • Некоторые события

  • CPU_CLK_UNHALTED.CORE – количество тактов процессора
  • INST_RETIRED.ANY – количество исполненных инструкций
  • BUS_TRANS_ANY.ALL_AGENTS – количество всех транзакций шины
  • L2_LINES_IN.SELF.DEMAND – количество промахов по L2 кэшу.
  • BR_INST_RETIRED.MISPRED – неправильно предсказанные ветвления
  • Здесь представлены названия некоторых счётчиков событий. Наиболее полезными для начала будут первые два – это такты процессора и количество исполненных инструкций. Ведь само по себе то, что ваша программа проводит 95% времени в одной функции и тратит на это миллионы тактов ещё ни о чём не говорит. Вот если за это время она успевает выполнить всего лишь тысячу инструкций, то где-то тут проблема. Соотношение CPU_CLK_UNHALTED / INST_RETIRED называется CPI (Clocks per Instruction) и оно позволяет грубо оценить насколько хорошо работает ваш код. Современные процессоры способны выполнять несколько инструкций за такт, так что это соотношение может быть значительно меньше единицы. Если же оно больше единицы, стоит посмотреть на это место внимательнее, возможно собрать дополнительную информацию. Например, может быть вы постоянно ждёте данных из памяти – об этом скажут следующие два события. От версии VTune и от того, какое "железо" вы используете, события могут называться по другому. Тем не менее в файлах помощи они все описаны и каталогизированы, какие события относятся к каким проблемам.

    Про то, что неправильное предсказание переходов ломает конвеер и логику Out-Of-Order Execution мы уже говорили. Последнее событие как раз позволит вам оценить, всё ли хорошо в вашем коде с этой точки зрения.

    Время доступа к памяти

    Небольшое отступление по поводу доступа к памяти. Эти графики показывают пропускную способность (bandwidth) и время ожидания (latency) при обращении к памяти. Сняты они были на стареньком Pentium M, сейчас всё получше, но идея та же. Вы видите чёткие ступеньки. Первая это пока мы работаем с кэшем первого уровня, вторая – данные перестали попадать в кэш первого уровня, но спасает кэш второго уровня. Затем (а на той машине кэш был всего двухуровневый) начинается непосредственно прямой доступ в память. Вначале нам всё ещё помогает аппаратная логика предподкачки данных (hardware prefetch), но она тоже не всесильна, плюс у шины данных тоже есть максимальная пропускная способность. Но это уже крайний пример и вряд ли он вам будет часто встречаться в повседневной разработке.

    Что влияет на то, насколько плохо использует ресурсы компьютера программа?

  • Конкуренция за ресурсы
  • "Узкие места" - bottlenecks
  • Зависимости (например, по данным)
  • Неиспользование каких-то возможностей (многоядерность, новые архитектурные возможности,..)
  • Итак, что у нас есть – конкуренция за общие ресурсы, такие как вычислительные устройства процессора. Особо к узким местам я хочу отнести какие-то постоянные синхронизации, которые не позволяют загрузить все ядра на полную мощность, а также просто неиспользование всего предлагаемого функционала. Кстати, это может происходить при переносе вручную написанного "высокооптимизированного" ассемблерного кода на новую платформу. Ну или при использовании неподходящего компилятора.

    Страницы:

    Презентацию к лекции Вы можете скачать здесь.

    Основные характеристики приложения, влияющие на его производительность

  • Эффективность вычислений
  • Эффективность работы с памятью
  • Правильное предсказание переходов
  • Эффективность использования векторных инструкций
  • Эффективность параллелизации
  • Уровень инструкционного параллелилизма
  • Под эффективностью вычислений подразумевается "не делать лишней работы", что достигается эффективным анализом потока данных. Избегать повторных сложных вычислений, протягивать и свертывать константы, удалять "мертвый код".

    Место и роль компилятора

    Компилятор — транслятор, который осуществляет перевод всей исходной программы в эквивалентную ей результирующую программу на языке машинных команд или на языке ассемблера.

    Основная задача оптимизирующего компилятора – получение кода максимально эффективного для используемого вычислительного комплекса.

    С точки зрения разработчика программа должна быть:

  • Легко читаемой и модифицируемой
  • Легко отлаживаемой
  • Быстро исполняемой
  • Разработчику необходима

  • надежная унифицированная среда разработки
  • возможность варьировать уровни отладки и быстродействия
  • возможность получать высокоэффективный код для различных операционных систем и микропроцессорных архитектур.
  • Компилятор должен удовлетворить эти требования.

    В настоящее время от компилятора требуется получение эффективного и высоконадежного кода. С точки зрения разработчика программа должна быть легко читаемой и модифицируемой. Для реализации этого требования в больших проектах необходима модульность, основная функциональность выносится в отдельные утилиты, используется объектно-ориентированное программирование и т.д. Благодаря этому оптимизация программного кода – сложная и комплексная задача. Дополнительный уровень сложности возникает из-за обилия поддерживаемых архитектур и различных расширений. В результате оптимизирующий компилятор сложный программный комплекс, включающий в себя большое разнообразие оптимизирующих техник.

    Оптимизирующий компилятор

    Это программный комплекс, работа которого варьируется в зависимости от требований к результирующему коду.

    Возникают следующие проблемы:

  • Сложность доказательства допустимости тех или иных оптимизаций
  • Сложность расчета выгодности оптимизаций
  • Отсутствие во время компиляции представления о типичных входных данных
  • Для достижения хороших результатов требуется тесное сотрудничество с разработчиком

    Чтобы использовать умело средства компилятора, программист должен:

  • иметь представления о архитектуре, на которой будет использоваться его программа
  • ознакомиться с настройками компилятора
  • ознакомиться с основными техниками улучшения производительности, которые использует компилятор
  • ознакомиться с основными проблемами, вызывающими замедление работы программы
  • знать примерные данные, с которыми будет работать программа
  • уметь пользоваться инструментами для анализа производительности программы.
  • Компиляторы Intel

    С/C++ и Fortran для операционных систем Windows, Linux и Mac OS

    Для Windows компилятор может быть интегрирован в Microsoft Visual Studio

    Главной целью корпорации является высокая производительность компиляторов

    и совместимость с Microsoft Visual Studio на Windows и с gcc на Linux и Mac OS.

    Некоторые полезные опции компилятора

  • /Od (-O0 for Linux) – оптимизации отключены, дебаг-режим.
  • /O2 (-O2 for linux) – оптимизации "по-умолчанию" .
  • /O3 (-O3 for linux) – дополнительные оптимизации.
  • /xO (-xO for Linux) – оптимизация под неинтеловскую архитектуру.
  • /Qipo (-ipo) - межпроцедурная оптимизация.
  • /Qparallel (-parallel) – автопараллелизация.
  • /Qopt-report (-opt-report)
  • /Qopt-report-file
  • /Qopt-report-phase
  • /Qopt-report-help
  • /Qopt-report-routine
  • /Qvec-report [1/2/3]
  • Очень часто, чтобы сделать приложение быстрее, надо просто выбрать правильный компилятор и установить ему правильные опции.

    Дополнительные инструменты оптимизации приложений

    Существуют различные средства поиска неоптимальных участков кода в зависимости от типов решаемых задач и сред исполнения

    Intel VTune™ Amplifier XE Performance Profiler

  • предоставляет информацию о производительности программ
  • подходит как для последовательных, так и для многопоточных приложений
  • версии как для Windows, так и для Linux
  • для Windows может интегрироваться в Visual Studio или работать отдельно с собственным GUI
  • для Linux – только отдельно
  • есть возможность использования из командной строки для удалённого сбора данных или регрессивного тестирования
  • Intel VTune – многофункциональный инструмент, предназначенный для анализа производительности приложения. Он позволяет получить полную информацию о том, как на самом деле работает ваша программа. Бывает, что даже опытный разработчик не всегда может верно оценить, на что тратятся основные вычислительные ресурсы и какие вычисления являются "узким местом", ограничивающим производительность всего приложения.

    Intel VTune™ Amplifier XE Performance Profiler

    Помогает проанализировать особенности алгоритма и определить фрагменты приложения, где оно может использовать доступные ресурсы более рационально.

    Позволяет найти и определить следующее:

  • функции, на вычисление которых тратится основное время (горячие функции)
  • фрагменты кода, которые не используют процессор эффективно
  • области, наиболее сильно нуждающиеся в оптимизации
  • синхронизации, влияющие на производительность приложения
  • фрагменты кода, создающие эффект бутылочного горлышка
  • Intel VTune особенно удобен тем, что для решения большинства своих задач он не требует пересборки программы. VTune – не статический анализатор, он собирает информацию о том, как работает интересующее приложение или вся система в реальных условиях, на реальных данных, "под нагрузкой". Желательно, что бы исследуемая программа была скомпилирована с сохранением отладочной информации. В этом случае можно будет "провалиться" вплоть до исходного кода и получить детальную характеристику каждой строчки исполняемого кода. Какая именно информация будет собрана и насколько она будет подробной, зависит от режима работы Intel VTune и от настроек. Далее мы познакомимся и с возможными режимами работами и с настройками. Отведённое нам время позволит это сделать лишь достаточно поверхностно, в виде ознакомления. Тем не менее основные моменты станут понятными, подробности вы всегда сможете узнать у самого VTune. VTune мощный и, вообще говоря, достаточно сложный инструмент. Точнее, с его помощью можно делать весьма сложные вещи, крайне трудно осуществимые без его помощи. Тем не менее, пользоваться им не сложно. Это инструмент с долгой историей, с набором дочерних и параллельных проектов, которые, в том числе, активно используются и внутри компании. Помимо документации VTune содержит отличный контекстный help, который поможет вам разобраться. Так же есть документация, как я уже сказал, также форумы поддержки и базы знаний Intel, ну и Google спешит на помощь?

    Почему здесь выделена Java? Потому что с подобными языками есть проблема. А именно, исполняемого кода в явном виде не существует. Исполняемый код создаётся Just In Time Compiler’ом прямо в памяти непосредственно во время работы приложения. Более того, со временем код может перекомпилироваться для получения лучшей производительности. Особенность VTune в том, что он умеет работать с такими вещами.

    Шаги по использованию VTune

  • Выбрать объект для анализа
  • Скомпилировать
  • Запустить анализ
  • Интерпретировать результаты
  • Улучшить приложение при помощи алгоритмов оптимизации
  • Сборка приложения

    Для получения более детальной информации посредством VTune рекомендуется компилировать приложения в режиме debug с генерацией символьной информации

    Запуск анализа

    Запуск из графического интерфейса осуществляется интуитивно понятно

    Анализ

  • После выполнения анализа отображается общая информация
  • Список содержит функции, на выполнение которых было затрачено наибольшее количество времени
  • Стек вызовов

    Можно просматривать не только функцию, но и стек вызова

    Просмотр по вычислительным потокам

  • 1 – время
  • 2 – вычислительные потоки
  • 3 – использование процессора
  • Исходный код

  • 1 – код программы, 2 – ассемблерный код,
  • 3 – процессорное время,
  • 4 и 5 – элементы навигации для быстрого перемещения по горячим строкам кода
  • Сравнение результатов

    Для определения выигрыша, вызванного оптимизациями удобно воспользоваться функцией сравнения

    Сравнение результатов

  • 1 – различие во времени между версиями
  • 2 – время до оптимизаций
  • 3 – время после оптимизаций
  • Оценка эффективности использования вычислительных ресурсов

    Аналогичным образом можно произвести другие виды анализа, например, locks and waits

    Осуществление анализа

    После выполнения анализа вам также будет представлена краткая информация, но уже по другому аспекту

    Осуществление анализа

    Также имеется возможность просмотра более детальной информации по стеку вызова и объектам синхронизации

  • 1 – объект,
  • 2 – использование процессора,
  • 3 – количество циклов ожидания
  • Анализ исходного кода

  • 1 – строки кода,
  • 2 – использование процессора,
  • 3 – общее количество циклов ожидания,
  • 4 - навигация
  • Сравнение результатов

  • 1 – различия во времени ожидания,
  • 2 – время ожидания до оптимизаций,
  • 3 – время ожидания после оптимизаций,
  • 4 – разница в количестве циклов ожидания,
  • 5 и 6 – количество циклов ожидания
  • Некоторые события

  • CPU_CLK_UNHALTED.CORE – количество тактов процессора
  • INST_RETIRED.ANY – количество исполненных инструкций
  • BUS_TRANS_ANY.ALL_AGENTS – количество всех транзакций шины
  • L2_LINES_IN.SELF.DEMAND – количество промахов по L2 кэшу.
  • BR_INST_RETIRED.MISPRED – неправильно предсказанные ветвления
  • Здесь представлены названия некоторых счётчиков событий. Наиболее полезными для начала будут первые два – это такты процессора и количество исполненных инструкций. Ведь само по себе то, что ваша программа проводит 95% времени в одной функции и тратит на это миллионы тактов ещё ни о чём не говорит. Вот если за это время она успевает выполнить всего лишь тысячу инструкций, то где-то тут проблема. Соотношение CPU_CLK_UNHALTED / INST_RETIRED называется CPI (Clocks per Instruction) и оно позволяет грубо оценить насколько хорошо работает ваш код. Современные процессоры способны выполнять несколько инструкций за такт, так что это соотношение может быть значительно меньше единицы. Если же оно больше единицы, стоит посмотреть на это место внимательнее, возможно собрать дополнительную информацию. Например, может быть вы постоянно ждёте данных из памяти – об этом скажут следующие два события. От версии VTune и от того, какое "железо" вы используете, события могут называться по другому. Тем не менее в файлах помощи они все описаны и каталогизированы, какие события относятся к каким проблемам.

    Про то, что неправильное предсказание переходов ломает конвеер и логику Out-Of-Order Execution мы уже говорили. Последнее событие как раз позволит вам оценить, всё ли хорошо в вашем коде с этой точки зрения.

    Время доступа к памяти

    Небольшое отступление по поводу доступа к памяти. Эти графики показывают пропускную способность (bandwidth) и время ожидания (latency) при обращении к памяти. Сняты они были на стареньком Pentium M, сейчас всё получше, но идея та же. Вы видите чёткие ступеньки. Первая это пока мы работаем с кэшем первого уровня, вторая – данные перестали попадать в кэш первого уровня, но спасает кэш второго уровня. Затем (а на той машине кэш был всего двухуровневый) начинается непосредственно прямой доступ в память. Вначале нам всё ещё помогает аппаратная логика предподкачки данных (hardware prefetch), но она тоже не всесильна, плюс у шины данных тоже есть максимальная пропускная способность. Но это уже крайний пример и вряд ли он вам будет часто встречаться в повседневной разработке.

    Что влияет на то, насколько плохо использует ресурсы компьютера программа?

  • Конкуренция за ресурсы
  • "Узкие места" - bottlenecks
  • Зависимости (например, по данным)
  • Неиспользование каких-то возможностей (многоядерность, новые архитектурные возможности,..)
  • Итак, что у нас есть – конкуренция за общие ресурсы, такие как вычислительные устройства процессора. Особо к узким местам я хочу отнести какие-то постоянные синхронизации, которые не позволяют загрузить все ядра на полную мощность, а также просто неиспользование всего предлагаемого функционала. Кстати, это может происходить при переносе вручную написанного "высокооптимизированного" ассемблерного кода на новую платформу. Ну или при использовании неподходящего компилятора.

    Вернуться к учебному плану