Хабраиндекс
428,99
15 ноября 2012 в 14:36

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



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

Про процессоры можно говорить много и подробно и, наверняка, среди читателей Хабра есть масса людей споcобных на такие разговоры. Но моя точка зрения на процессор сугубо прагматичная. Поскольку меня интересует производительность приложения, через призму производительности процессора, то мне достаточно понимания базовых принципов работы вычислительного ядра. А также методов, которые существуют, чтобы на эти базовые принципы воздействовать. Буду я ориентироваться на архитектуру Intel64. Это вызвано тем, что в нашей команде анализа производительности мы занимаемся анализом работы оптимизирующего компилятора Intel, в основном, именно для этой архитектуры. На рынке вычислительных систем для высокопроизводительных вычислений эта и совместимые архитектуры занимают львинную долю, поэтому большинство проблем производительности имеет довольно общую природу. Давайте я коротко перечислю те основные проблемы и возможности, которые определяют производительность ядра и вычислительной системы и предложу короткий список различных оптимизаций, призванных влиять на эти проблемы.



Уровень инструкционного параллелизма



Современное вычислительное ядро характеризуют наличие конвейера (pipelining), суперскалярности. Вычислительное ядро обладает механизмом для определения независимых команд и планирования инструкций. Т.е. эти механизмы просматривают буфер прибывших на обработку инструкций и выбирают их для исполнения, если есть подходящие вычислительные мощности и инструкции не зависят от других еще не выполненных инструкций. Таким образом для Intel64 архитектуры характерено внеочередное исполнение команд (out-of-order execution). Т.е. для такой архитектуры компилятору не нужно заниматься детальным определением порядка инструкций. Уровень загрузки конвейера будет определяться тем, насколько много независимых инструкций попадает на обработку, так называемым уровнем инструкционного параллелизма.

Количество условных переходов и качество работы предсказателя переходов



Порядок выполнения различных команд определяется графом потока управления, который связывает прямолинейные участки кода. В местах где происходит ветвление потока управления используются, как правило, различные условные переходы. Т.е до момента вычисления условия, непонятно какая инструкция должна выполняться следующей. Для того, чтобы работа конвейера не прерывалась, в вычислительном ядре существует механизм предсказания ветвления, который выбирает один из возможных путей передачи управления и продолжет доставлять на вычислительный конвейер инструкции с этого направления. Уже выполненные инструкции дожидаются валидации, которая происходит после вычисления условия. Если же предсказатель ошибся, то происходит задержка в работе вычислительного ядра, вызванная необходимостью очистить буфера вычислительного ядра и загрузить в них новые инструкции. Такая ситуация называется ошибкой предсказания (branch misprediction). Механизм предсказания ветвления довольно сложен и включает как статическую часть, которая делает попытку угадать направление при первой встрече с условным переходом, так и динамическую, накапливающую и использующую статистику.

Качество работы подсистемы памяти



Поскольку все основные данные и инструкции располагаются в оперативной памяти, то для производительности важно, за какое время эти данные могут быть доставлены в вычислительное устройство. В данном случае, задержка на получение данных из основной памяти составляет сотни процессорных циклов. Чтобы ускорить работу процессор распологает кэшем или подсистемой кэшей. Кэш хранит копии часто используемых данных из основной памяти. Для современных многоядерных процессоров типична ситуация, когда ядро владеет кэшем первого и второго уровня, а процессор имеет также кэш третьего уровня, который ядра используют совместно. Кэш первого уровня самый маленький по размерам и быстрый, кэши верхней иерархии медленнее, но больше. Процессоры архитектуры Intel64 имеют инклюзивный кэш. Это значит, что если какой-то адрес представлен в кэше какого-либо уровня, то он содержится и в кэшах верхней иерархии. Поэтому при необходимости получения данных из памяти, подсистема памяти последовательно проверяет наличие адресов в кэше первого, второго и третьего уровня. В случае промаха по кэшу (т.е. отсутствии необходимой информации в кэше) в каждом последующем случае задержка будет больше. Например, для Nehalemа приблизительное время доступа к кэшам разного уровня будет следующим: 4 для кэша первого уровня и соответственно 11 и 38 для кешей второго и третьего уровня. Задержка при доступе к оперативной памяти составляет приблизительно от 100 до 200 циклов. Для понимания работы подсистемы памяти важен факт, что передача и хранение необходимой информации в кэше происходит пакетами фиксированной длины, которые называются кэш линиями и составляют (пока?) 64 байта. При запросе определенного адреса памяти в кэш загружаются «попутно» какие-то «соседние» данные. Системная шина осуществляет передачу данных между подсистемой кэшей и CPU. Важной характеристикой подсистемы памяти является пропускная способность шины, т.е. сколько кэш линий может быть передано в кэш за единицу времени. Соответственно, работа системной шины часто становится «узким местом» при выполнении приложения.
В связи с такой организации кэша большую роль играет такой фактор как принцип локальности (locality of reference) используемых программой данных. Различают временную локальность (temporal locality), характеризующую переиспользование в процессе работы приложения основных объектов и данных и пространственную локальность (spatial locality) – использование данных, имеющих относительно близкие области хранения. И в вычислительном ядре реализованы механизмы для поддержки принципа локальности. Временную локальность поддерживает механизм кэширования, который стремиться сохранять в кэшах наиболее часто используемые данные. Когда встает вопрос о необходимости освободить кэш линию для загрузки новой, то удаляется та линия, которая не использовалась наибольшее время. На пространственную локальность работает механизм упреждающей выборки, который стремиться определить заранее адреса памяти, которые потребуются для последующей работы. При этом, чем выше пространственная локальность, (идет доступ к соседним элементам), тем меньше данных нужно закачивать в кэш, тем меньше нагрузка на системную шину.
Можно также здесь упомянуть регистры вычислительного ядра, как наибыстрейшую память. Выгодно в регистрах хранить переиспользуемые данные. Существует такая проблема производительности как вытеснение регистров (register spilling), когда при вычислениях постоянно идет копирование данных из регистров на стек и обратно.

image

Использование векторизации



Процессоры архитектуры intel64 поддерживают векторные инструкции. Т.е. существует возможность создания векторов различных типов и применение к ним инструкций работающих с векторами и получающих на выходе вектор результатов. Это инструкции семейства SSE. Набор инструкций развивается, дополняется новыми командами и возможностями. SSE2, SSE3, SSE4, AVX – расширения первоначальной идеи. Проблема заключается в том, что языки программирования у нас изначально скалярные и нужно делать определенные усилия, чтобы использовать эту возможность вычислительных ядер. Векторизация — это модификация кода, которая заменяет скалярный код на векторный. Т.е. скалярные данные упаковываются в вектора и скалярные операции заменяются на операции с векторами (пакетами). Можно использовать xmmintrin.h для программирования векторизации вручную, использовать какой-то оптимизирующий компилятор с автовекторизацией или оптимизированные библиотеки с утилитами, использующими векторные инструкции. Существует масса ограничений на возможность применения этой модификации. К тому-же есть много факторов определяющих ее выгодность. Поэтому я и написал модификация, поскольку оптимизация – это то, что улучшает производительность. Чтобы векторизация ее улучшала, необходимо выполнение некоторых условий. Т.е. векторизация – это всего лишь «возможность». Нужно потрудиться чтобы воплотить эту возможность в реальные достижения.

Использование многопотоковых вычислений



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

На основе перечисленных проблем можно выделить 3 основных характеристики определяющих производительность выполняемого приложения и 2 большие возможности, которые могут быть реализованы:
1.) Качество работы подсистемы памяти.
2.) Количество условных переходов и качество работы предсказателя переходов.
3.) Уровень инструкционного параллелизма.
4.) Использование векторизации.
5.) Использование многопотоковых вычислений.

Оптимизации



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

Улучшить работу подсистемы памяти призваны многие цикловые оптимизации, такие как перестановка циклов, разбиение циклов на блоки (loop blocking), объединение и разбиение циклов (loop fusion/distribution). У этих оптимизаций есть недостаток. Не все циклы подходят для таких оптимизаций и в основном они встречаются в вычислительных программах на C и фортране. Компилятор может попытаться помочь механизму упреждающей выбоки и автоматически вставить програмные инструкции предвыборки, но опять же – это очень специализированная операция, требующая благоприятного расположения звезд. Если строится исполняемый модуль, то с динамическим профилировщиком компилятор умеет улучшать пространственную локальность данных приложения за счет перестановки полей структур и выноса холодных областей в отдельные структуры. Есть даже такая оптимизация, как перенос полей из одной структуры в другую. Компилятору достаточно трудно доказать корректность и выгодность таких оптимизаций, но программисту делать такие вещи гораздо легче, главное понимать в чем идея той или иной модификации кода.
Важную роль в улучшении локальности данных могут играть скалярные оптимизации, т.е. оптимизации занимающиеся анализом потока данных, протяжкой констант, оптимизацией графа управления, удалением общих подвыражений, удаление мертвого кода. Эти оптимизации сокращают количество вычислений, которые необходимо выполнить приложению. В результате сокращается количесво инструкций и улучшается локальность данных, поскольку инструкции так-же нужно хранить в кэше. Качество выполнения этих оптимизационных методов можно назвать эффективностью вычислений. Всем этим оптимизациям помогает межпроцедурный анализ. В случае его использования, анализ потока данных становится глобальным, т.е. анализируется перенос свойств объектов не только на уровне конкретной обрабатываемой компилятором процедуры, но и внутри всей программы. Изучаются и используются внутри процедуры свойства конкретных аргументов, с которыми эта процедура вызывалась из разных мест приложения, изучаются свойства глобальных объектов, для каждой функции создается список объектов, которые могут быть использованы или модифицированы внутри этой функции, и т. д. Подстановка тела функции (inlining) и частичная подстановка так-же может положительно сказаться на локальности данных. Многие из вышеперечисленных оптимизаций уже не сделаешь руками, поскольку эффект от каждой протянутой константы может быть мизерным, но в итоге протяжка констант дает хороший суммарный эффект и зачастую дает возможность применять и более правильно оценивать эффект от более сложных оптимизаций.
Кроме того, оптимизирующий компилятор много внимания уделяет распределению регистров.

Оптимизирующий компилятор может выполнять определенные действия и для снижения количества условных переходов и качества работы предсказателя переходов. Самое тривиальное — улучшение работы статического предсказателя ветвления за счет изменения условия при котором происходит переход. Статический предсказатель исходит из того, что если при ветвлении графа управления возможен переход вниз, то он не будет выполнен. Если компилятор считает, что вероятность ветки if меньше, чем для else, то он может поменять их местами. Очень сильной оптимизацией является вынос инвариантной проверки из циклов. Еще одна известная оптимизация — развертка циклов (loop unrolling). Существует полезная скалярная оптимизация, которая протягивает по графу управления условия, что позволяет удалять излишние проверки, объединять их и упрощать граф управления. Также компилятор способен распознавать определенные паттерны и заменять ветвления на использования инструкций типа cmov. Наверное, можно привести еще примеры, но и этот показывает, что оптимизирующий компилятор имеет методы для борьбы с задержками из-за неправильных предсказаний.

Для улучшения инструкционного параллелизма служит планирование (scheduling), выполняемое на последних стадиях работы компилятора при кодогенерации.

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

Компилятор Intel предлагает разработчикам использовать автопараллелизацию – механизм автоматического распараллеливания циклов. Это, по сути, самый дешевый в плане затрат труда способ создать многопоточное приложение – запустить построение приложения со специальной опцией. Но с точки зрения оптимизируещего компилятора это непростая задача. Необходимо доказать допустимость оптимизации, необходимо правильно оценить объем работы, выполняемой внутри цикла, чтобы решение о параллелизации улучшило производительность. Автоматическая параллелизация так же как и автовекторизация должна взаимодействовать с другими цикловыми оптимизациями. Наверное, в этой области наилучшие оптимизационные алгоритмы еще не написаны, и простор для экспериментов и новаторских идей по прежнему велик. Для развития более наглядных для программистов методов параллелизации создаются новые языковые расширения и различные библиотеки поддержки, такие как CILK+ или Threading Building Blocks. Ну и добрый и проверенный OPENMP тоже рано сбрасывать со счетов.

Заключение:



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

Векторизуется ли vector (маленький пример анализа)



Иногда даже одно и то же приложение может показать совершенно удивительные результаты при анализе производительности. Вот, например, зададимся вопросом векторизуется ли vector? Рассмотрим вот такой тест:

#include <vector>
using namespace std;

int main ()
{
  vector<float> myvector;
  vector<float>::iterator it;

  for(int j=0; j<1000; j++) {
    it = myvector.end();
    it = myvector.insert ( it , j );
  }

  for (int i=1; i < myvector.size(); ++i)
    myvector[i]++;

  printf("%f\n",myvector[0]);
  return 0;
}



Я использую интеловский компилятор интегрированный в VS2008 для компиляции данного текста. В данном случае меня интересует вопрос, а весторизуется ли цикл в 14 строке кода. Используем опцию –Qvec_report3, чтобы посмотреть отчет автовекторизатора:

icl -Qvec_report3 -Qipo -Qansi_alias vector.cpp
Intel® C++ Intel® 64 Compiler XE for applications running on Intel® 64, Version Mainline Beta Build x

...\vector.cpp(14): (col. 30) remark: loop was not vectorized: unsupported loop structure.


Теперь я использую тот-же компилятор, но уже интегрированный в VS2010.

icl -Qvec_report3 -Qipo -Qansi_alias vector.cpp

…\vector.cpp(14): (col. 30) remark: LOOP WAS VECTORIZED.


Я здесь не буду рассказывать о том, какими путями можно докопаться здесь до причины разного поведения компилятора при компиляции одной и той же программы. (Можно посмотреть ассемблер, можно препроцессированный код). Поскольку интеловский компилятор интегрируется в разные версии VS, то в данном случае используются разные версии STL библиотеки. В версии STL VS2008 оператор [] содержит проверку выхода за границы массива. В результате, внутри цикла появляется вызов процедуры __invalid_parameter_noinfo с выходом из программы и компилятор не в состоянии векторизовать такой цикл. А в VS2010 такой проверки уже нет. Интересно почему?
Забавно, что если убрать опцию –Qansi_alias, то векторизация тоже пропадает. То же самое происходит, если создавать вектор для хранения целых чисел, а не вещественных. Но причины этих «чудес» повод для отдельной статьи.

Первоисточники
Литература по исследованию различных вопросов производительности: The Software Optimization Cookbook, Second Edition или The Software Vectorization Handbook.
Базовые источники по различным техникам оптимизаций: Randy Allen, Ken Kennedy. Optimizing compilers for modern architectures, а также David F. Bacon, Susan L. Graham, Oliver J. Sharp. Compiler transformations for High-Performance Computing.
Для программистов, заинтересованных в программном улучшении кода, можно рекомендовать книгу Agner Fog, Optimizing software in C++: An optimization guide for Windows, Linux and Mac platforms.
Не постеснялся скопировать какие-то мысли из своей статьи «Оптимизирующие компиляторы» в журнале «Открытые системы».
+14
11993
112
andrei_an 10,5

Комментарии (13)

+5
gribozavr, #
Ну это же блог компании Intel, а на дворе 2012 год. Откуда на картинке взялась «системная шина»? Из 1995-го?

> Т.е. эти механизмы просматривают буфер прибывших на обработку инструкций и выбирают их для исполнения, если есть подходящие вычислительные мощности и инструкции не зависят от других еще не выполненных инструкций. Таким образом для Intel64 архитектуры характерено внеочередное исполнение команд (out-of-order execution).

Вы описали суперскалярность, которая может быть in order. OOO execution из этого не следует («таким образом»), хотя конечно применяется в современных x86.

> Т.е. для такой архитектуры компилятору не нужно заниматься детальным определением порядка инструкций.

Ой, ещё и как нужно! Есть определённое окно, которое процессор просматривает вперёд. Если в пределах этого окна не будет подходящих команд, то хоть ILP и присутствует, но процессором замечено не будет.

Хотя конечно для получения приличных результатов достаточно делать scheduling в пределах базового блока, когда для VLIW нужно уже смотреть глобальнее.

> Существует такая проблема производительности как вытеснение регистров (register spilling), когда при вычислениях постоянно идет копирование данных из регистров на стек и обратно.

А при чём тут это в разделе про кеш?

> Всем этим оптимизациям помогает межпроцедурный анализ. В случае его использования, анализ потока данных становится глобальным, т.е. анализируется перенос свойств объектов не только на уровне конкретной обрабатываемой компилятором процедуры, но и внутри всей программы.

Inter-procedural analysis и whole-program analysis — очень разные вещи. Первая в компиляторах реализуется относительно просто, а вторая — очень сложно.

> Векторизуется ли vector

std::valarray же. Именно поэтому его в стандарт добавили.

А вообще с моей точки зрения статья содержит набор малосвязанных фактов. То, что касается вводной части про архитектуру и так уже сто раз везде написано, а что касается внутренностей компилятора — вы написали в основном только названия оптимизаций (иногда довольно спорные переводы их названий на русский), которые неспециалисту ни о чём не говорят.
0
mark_ablov, #
> которые неспециалисту ни о чём не говорят.
Ну я хоть и неспециалист, но почти всё понял :)
Разве что не думал что с развитием блока предсказаний еще используют loop-unroll, ведь есть еще и векторизация, которая тоже может заменять развертку в ряде задач.
0
andrei_an, #
Векторизация не всегда применима. К тому-же и для векторизованного цикла тоже можно сделать unroll. Так-же существует полная развертка небольших циклов с известным количеством итераций — исключительно полезная оптимизация. Как раз при векторизации часто возникают такие циклы. Поэтому развертка активно используется.
+4
andrei_an, #
1.) Если открыть, например, Intel® 64 and IA-32 Architectures Software Developer’s Manual, то там в разделе Basic Architecture понятие System Bus активно используется, как механизм соединяющий вычислительное ядро с памятью и остальным процессором. В этом виде этот термин меня вполне устраивает. Могли бы вы объяснить, какой, на ваш взгляд, термин нужно использовать и почему?
2.) Суперскалярность — это наличие нескольких исполняющих устройств. В данном случае описан механизм выбоки из буфера просмотра инструкций на обработку (То что вы в своем следующем вопросе называете определенным окном). В случае in order, насколько я понимаю, нет необходимости просматривать какие-то буфера и что-то выбирать, инструкции поступают на обработку в порядке определенном кодогенератором.
3.) Именно этот механизм здесь и описан. «Не нужно определять детальный порядок» и означает, что планированием инструкций нужно заниматься ориентируясь на буфер просмотра (look-ahead buffer). По идее механизмы внеочередного выполнения команд должны примерно одинаково отработать если инструкции будут перемешаны в пределах этого буфера. Наверное, в реальности все несколько сложнее, но идея такая.
4) Рассматривается не кэш, а качество работы подсистемы памяти и регистры туда включены на правах самой быстрой памяти.
5) В соответствии с моими представлениями, именно в процессе межпроцедурного анализа определяется имеет ли компилятор дело с полной программой или каким-то неполным графом вызовов. В случае если компилируется полная программа включаются дополнительные межпроцедурные оптимизации и делается более качественный межпроцедурный анализ. То есть из моей практики не следует, что это очень разные вещи. Действительно при компиляции неполной программы сужается набор возможных оптимизаций.
6) Спасибо за информацию. Но не вижу теоритически никаких препятствий для векторизации vector. То, что он не векторизуется или скорее векторизуется при выполнении сложных условий является недостатком компилятора. Посмотрю как компилятор справляется с valarray.
7) А я планирую развивать тему, поэтому двигаюсь планомерно.Спорные переводы возникают во многом оттого, что есть некий недостаток в справочной информации в Интернете. Описания идей многих оптимизаций присутсвуют только на английском как и практически вся используемая мной техническая литература.
Спасибо за критику.
+2
mark_ablov, #
> ) Если открыть, например, Intel® 64 and IA-32 Architectures Software Developer’s Manual, то там в разделе Basic Architecture понятие System Bus активно используется, как механизм соединяющий вычислительное ядро с памятью и остальным процессором.

Ну gribozavr погорячился слегка с 1995, но последние, как минимум, 3 поколения system bus не используется, в спеке показана system bus для netburst/P6/c2d архитектур.
Сейчас ядра (сами блоки, вместе с per-core переферией, типа кэшов) замкнуты кольцом, как в cell'ах, а к памяти и io ведут различные шины, к ddr3-слотам одна шина, к PCI-E — другая, к PCH — третья.
Термин — шина памяти, не?
0
andrei_an, #
Спасибо. Действительно, логичнее использовать термин шина памяти.
+1
gribozavr, #
5. IPA и WPA разные вещи. WPA позволяет нам, например, применять такие трансформации как, например, удаление ненужных аргументов у функций с видимостью за пределы единицы трансляции, или изменение лейаута структур в памяти, или использование fat pointers для какой либо цели.

Это не очень разные вещи с точки зрения написания оптимизаторов, применимых для IPA, в случае если у вас архитектура WPA построена на сериализации промежуточного представления в объектные файлы и последующий WPA десериализованного промежуточного представления во время линковки. Тогда вы действительно производите как бы IPA но для всей программы (но при этом можете и не применять whole-program оптимизации — эффект от расширения скоупа IPO уже и так будет).

3. Буфер просмотра постоянно сдвигается. Почему я зацепился за эту тему: просто «Не нужно определять детальный порядок» намекает, что это просто, хотя на самом деле это не просто и от этого сильно зависит производительность результирующего кода.

4. Буду придираться: в заголовке да, про качество подсистемы памяти. А в тексте страница про кеш и в конце было одно предложение про регистры.

И кроме того, компилятор работает с регистрами и учитывает наличие кеша совершенно по-разному. Например, register spilling — это проблема уровня компиляции и её ещё хоть как-то можно исправить. Аналогичную проблему с кешем (например, недостаточно ассоциативности и хоть в кеше есть свободные строки, но нужные вам данные постоянно вытесняют друг друга) можно решать только на уровне микроархитектуры.

6. valarray спроектирован так, чтобы «традиционный» векторизирующий компилятор без особого интеллекта мог его векторизировать.

7. Совершенно согласен, общепринятых переводов многих терминов на русский просто нет. Обычно также указывают оригинальный термин чтобы читатель не занимался обратным переводом.
0
andrei_an, #
5.) Наверное, это все же вопрос терминологии. WPA оптимизации ведь тоже применяются на графе вызовов и связаны с глобальной протяжкой разной информации с использованием этого графа. Т.е. если считать работу с графом вызовов IP работой, то WPA просто специализированные межпроцедурные оптимизации (оптимизации на графе вызовов) учитывающие важный дополнительный фактор.
Но вообще, я согласен с этим замечанием и буду впредь разделять эти два вида оптимизаций.
3.) Я это понимаю и не вкладывал во фразу «не нужно определять детальный порядок» смысл, что планирование инструкций — это просто. В дальнейшем я упоминаю планирование как важную оптимизацию.
4.) Хорошо, объединие регистров и подсистемы памяти неудачное, поскольку методы разрешения проблем с кэшем и регистрами совершенно разные.
7.) В некоторых случаях я так и делаю, но и частое использование оригинальных терминов не украшает текст.
0
mark_ablov, #
> эффект от расширения скоупа IPO уже и так будет
структуры уже не переделать, например., поэтому имхо переходить на WPA во время компоновки уже поздновато малость.
0
gribozavr, #
> переходить на WPA во время компоновки уже поздновато малость

Совсем нет, главное просто сериализовать внутреннее представление (и не генерировать никакой машинный код) в объектные файлы.

А я говорил о том, что чем больше IPA видит, тем лучше (например, тем лучше инлайнинг).
0
andrei_an, #
В интеловском компиляторе сейчас два режима работы ipo. В первом случае с -Qipo создаются объектные файлы, которые содержат упакованное внутреннее представление. Во время второй фазы компилятор читает все эти файлы, строит граф вызовов, делает межпроцедурный анализ, затем межпроцедурные оптимизации и WPA (если удается доказать что работаем с полной программой) и затем уже все процедуры обрабатываются (различные скалярные и цикловые оптимизации) и создаются файл ipo.obj (или несколько ipo_n.obj). Второй вариант используется с -Qip. В этом случае межпроцедурный анализ работает с одним исходным файлом и делает оптимизации на основе того графа вызовов, который удается построить. Первый вариант должен давать лучшие результаты, но второй часто используется, поскольку он менее требователен к ресурсам.
+2
spiff, #
Оберните пожалуйста листинги тегами <source lang="cpp"> ... </source>.
0
andrei_an, #
Спасибо.

Только зарегистрированные пользователи могут оставлять комментарии.
Войдите, пожалуйста.