Как стать автором
Обновить

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

Время на прочтение12 мин
Количество просмотров17K
Всего голосов 28: ↑21 и ↓7+14
Комментарии13

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

Ну это же блог компании 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 же. Именно поэтому его в стандарт добавили.

А вообще с моей точки зрения статья содержит набор малосвязанных фактов. То, что касается вводной части про архитектуру и так уже сто раз везде написано, а что касается внутренностей компилятора — вы написали в основном только названия оптимизаций (иногда довольно спорные переводы их названий на русский), которые неспециалисту ни о чём не говорят.
> которые неспециалисту ни о чём не говорят.
Ну я хоть и неспециалист, но почти всё понял :)
Разве что не думал что с развитием блока предсказаний еще используют loop-unroll, ведь есть еще и векторизация, которая тоже может заменять развертку в ряде задач.
Векторизация не всегда применима. К тому-же и для векторизованного цикла тоже можно сделать unroll. Так-же существует полная развертка небольших циклов с известным количеством итераций — исключительно полезная оптимизация. Как раз при векторизации часто возникают такие циклы. Поэтому развертка активно используется.
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) А я планирую развивать тему, поэтому двигаюсь планомерно.Спорные переводы возникают во многом оттого, что есть некий недостаток в справочной информации в Интернете. Описания идей многих оптимизаций присутсвуют только на английском как и практически вся используемая мной техническая литература.
Спасибо за критику.
> ) Если открыть, например, 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 — третья.
Термин — шина памяти, не?
Спасибо. Действительно, логичнее использовать термин шина памяти.
5. IPA и WPA разные вещи. WPA позволяет нам, например, применять такие трансформации как, например, удаление ненужных аргументов у функций с видимостью за пределы единицы трансляции, или изменение лейаута структур в памяти, или использование fat pointers для какой либо цели.

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

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

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

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

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

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

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

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