5. IPA и WPA разные вещи. WPA позволяет нам, например, применять такие трансформации как, например, удаление ненужных аргументов у функций с видимостью за пределы единицы трансляции, или изменение лейаута структур в памяти, или использование fat pointers для какой либо цели.
Это не очень разные вещи с точки зрения написания оптимизаторов, применимых для IPA, в случае если у вас архитектура WPA построена на сериализации промежуточного представления в объектные файлы и последующий WPA десериализованного промежуточного представления во время линковки. Тогда вы действительно производите как бы IPA но для всей программы (но при этом можете и не применять whole-program оптимизации — эффект от расширения скоупа IPO уже и так будет).
3. Буфер просмотра постоянно сдвигается. Почему я зацепился за эту тему: просто «Не нужно определять детальный порядок» намекает, что это просто, хотя на самом деле это не просто и от этого сильно зависит производительность результирующего кода.
4. Буду придираться: в заголовке да, про качество подсистемы памяти. А в тексте страница про кеш и в конце было одно предложение про регистры.
И кроме того, компилятор работает с регистрами и учитывает наличие кеша совершенно по-разному. Например, register spilling — это проблема уровня компиляции и её ещё хоть как-то можно исправить. Аналогичную проблему с кешем (например, недостаточно ассоциативности и хоть в кеше есть свободные строки, но нужные вам данные постоянно вытесняют друг друга) можно решать только на уровне микроархитектуры.
6. valarray спроектирован так, чтобы «традиционный» векторизирующий компилятор без особого интеллекта мог его векторизировать.
7. Совершенно согласен, общепринятых переводов многих терминов на русский просто нет. Обычно также указывают оригинальный термин чтобы читатель не занимался обратным переводом.
> в windows очень много стороннего не опенсорсного proprietary — хотите ли вы, чтоб ваша программа, использующая что-то из того, обвалилась в никуда
О, интересно: проприетарностью кода оправдывают «невысокое» качество этого «кода».
> Второй момент, что многие средства разработки предоставляют механизмы словить такой доступ к памяти на стадии разработки — например VS при дебаге в free забивает освобожденную память определенной дрянью
Ага, позволяют. Только пример неудачный: забивать память определённым паттерном — это прошлый век. Берите современные инструменты, например, AddressSanitizer.
Ну это же блог компании 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 же. Именно поэтому его в стандарт добавили.
А вообще с моей точки зрения статья содержит набор малосвязанных фактов. То, что касается вводной части про архитектуру и так уже сто раз везде написано, а что касается внутренностей компилятора — вы написали в основном только названия оптимизаций (иногда довольно спорные переводы их названий на русский), которые неспециалисту ни о чём не говорят.
И это при том, что бекенды для компиляторов пишут в том числе инженеры из компаний, разрабатывающих процессоры, пользуясь не публичной информацией о внутреннем устройстве процессоров.
> Значит, у нас есть сегмент кода, данных, стека и т.д.
Нет. Это в исполняемом файле есть секции кода и данных. Они мапятся в плоское адресное пространство. Плоское (flat) адресное пространство плоское потому что у нас всего один *сегмент*, на который указывают все сегментные регистры (cs, ds, ss). Поэтому для интерпретации адреса не нужно знать из какого он сегмента, потому что сегмент один и тот же.
Но кроме исполняемого файла есть, например, библиотеки со своими сегментами. И всё это посегментно мапится в адресное пространство. При этом сегменты кода, естественно, не идут последовательно. И сегменты данных тоже. (см. опять таки /proc/PID/maps)
> У программы есть brk, который двигает end_data_segment.
Эмм… С точки зрения программиста — да, системный вызов brk что-то там двигает. На практике — он просто мапит/анмапит страницы в конец того что вы называете «сегментом данных».
brk [1] вызывает do_brk [2], который в результате создаёт anonymous mapping.
> Ничего не понимаю. Что такое «замапленные страницы»? Давайте для начала определимся, мы про файлы, или про анонимный мапинг?
Нет принципиальной разницы. В файловом маппинге backing store — файл, в анонимном — swap.
Есть страницы замапленные (cat /proc/self/maps) и незамапленные, обращение к которым ведёт к segfault.
> При вызове анонимного мапинга нужно передать свой указатель на доступный виртуальный адрес.
И передача этого адреса является только советом для mmap() и ничего не гарантирует. Этот адрес может быть не замаплен, но, например, ОС решила запретить его мапить (например, нулевой адрес — см. sysctl vm.mmap_min_addr) или процессор может не поддерживать его мапить (http://en.wikipedia.org/wiki/X86-64#Virtual_address_space_details).
> Чтобы адрес стал доступным, надо его разрешить — то есть поднять break point.
Нет единственного break point. Посмотрите /proc/self/maps у чего-нибудь посложнее, чем ls (например firefox). Страницы данных мапятся вплоть до гранулярности в 1 страницу.
> Насчёт sbrk — с ним могут работать несколько аллокаторов сразу, правда, dallocate при этом не получится, да.
Только если нет TOCTOU гонки между проверкой текущего break и установкой нового.
Нет никакого brk. Есть замапленые и незамапленые страницы. А преимущество например, в том, что с mmap() может быть несколько аллокаторов, а с sbrk() только один.
> Бред, такого быть не может, т.к. не может быть никогда.
Теоретически — может. Компилятор может вызывать разные malloc() или malloc() может раскручивать стек. Конечно на практике, так никто делать не будет так как нет смысла.
> Память либо доступна в рамках всего процесса, либо нет, точка.
А то, что не производится нормальная дефрагментация кучи — это конечно же баг.
> Однако при попытке выделить память из другого класса (либо статической функции) мы получим исключение — не достаточно памяти. По всей видимости при выделении памяти из статической функции память выделяется не в том же хипе, что и при обычном выделении изнутри класса приложения.
Также интересно что автор имел ввиду. Или его компилятор действительно вызывает разные malloc() в этих случаях (в чём я сомневаюсь), или написанное не соответствует наблюдениям.
> 520168 байт и выше — освобождение проходит нормально
Вы бы сначала почитали как работают аллокаторы памяти прежде чем писать «разгромную» статью. Это же классическое поведение аллокатора: маленькие объекты выделяются в общей куче, а за большими — обращаемся к системному аллокатору.
Не «под», а «для». И это и называется — написать бекэнд. Тот факт, что для вас слово бекэнд ничего не значит — ещё одно доказательство того, что вы не в теме.
Не нужно модифицировать языки. Нужно просто написать бекэнд.
Чем больше вы пишете, тем больше у меня впечатление что вы или вообще не в теме современных подходов к разработке компиляторов или всё намеренно делали в абсолютном информационном вакууме, согласно традиции вашей компании делать велосипеды.
Это не очень разные вещи с точки зрения написания оптимизаторов, применимых для IPA, в случае если у вас архитектура WPA построена на сериализации промежуточного представления в объектные файлы и последующий WPA десериализованного промежуточного представления во время линковки. Тогда вы действительно производите как бы IPA но для всей программы (но при этом можете и не применять whole-program оптимизации — эффект от расширения скоупа IPO уже и так будет).
3. Буфер просмотра постоянно сдвигается. Почему я зацепился за эту тему: просто «Не нужно определять детальный порядок» намекает, что это просто, хотя на самом деле это не просто и от этого сильно зависит производительность результирующего кода.
4. Буду придираться: в заголовке да, про качество подсистемы памяти. А в тексте страница про кеш и в конце было одно предложение про регистры.
И кроме того, компилятор работает с регистрами и учитывает наличие кеша совершенно по-разному. Например, register spilling — это проблема уровня компиляции и её ещё хоть как-то можно исправить. Аналогичную проблему с кешем (например, недостаточно ассоциативности и хоть в кеше есть свободные строки, но нужные вам данные постоянно вытесняют друг друга) можно решать только на уровне микроархитектуры.
6. valarray спроектирован так, чтобы «традиционный» векторизирующий компилятор без особого интеллекта мог его векторизировать.
7. Совершенно согласен, общепринятых переводов многих терминов на русский просто нет. Обычно также указывают оригинальный термин чтобы читатель не занимался обратным переводом.
Вот только не нужно пытаться исправиться. Вы сказали то, что сказали:
> в windows очень много стороннего не опенсорсного proprietary — хотите ли вы, чтоб ваша программа, использующая что-то из того, обвалилась в никуда,
> для сей/сей++ под виндами использую Rational Purify — ваш Sanitizer там даже близко не лежал.
И какой оверхед у Purify, использующего динамическое инструментирование?
О, интересно: проприетарностью кода оправдывают «невысокое» качество этого «кода».
> Второй момент, что многие средства разработки предоставляют механизмы словить такой доступ к памяти на стадии разработки — например VS при дебаге в free забивает освобожденную память определенной дрянью
Ага, позволяют. Только пример неудачный: забивать память определённым паттерном — это прошлый век. Берите современные инструменты, например, AddressSanitizer.
> Т.е. эти механизмы просматривают буфер прибывших на обработку инструкций и выбирают их для исполнения, если есть подходящие вычислительные мощности и инструкции не зависят от других еще не выполненных инструкций. Таким образом для Intel64 архитектуры характерено внеочередное исполнение команд (out-of-order execution).
Вы описали суперскалярность, которая может быть in order. OOO execution из этого не следует («таким образом»), хотя конечно применяется в современных x86.
> Т.е. для такой архитектуры компилятору не нужно заниматься детальным определением порядка инструкций.
Ой, ещё и как нужно! Есть определённое окно, которое процессор просматривает вперёд. Если в пределах этого окна не будет подходящих команд, то хоть ILP и присутствует, но процессором замечено не будет.
Хотя конечно для получения приличных результатов достаточно делать scheduling в пределах базового блока, когда для VLIW нужно уже смотреть глобальнее.
> Существует такая проблема производительности как вытеснение регистров (register spilling), когда при вычислениях постоянно идет копирование данных из регистров на стек и обратно.
А при чём тут это в разделе про кеш?
> Всем этим оптимизациям помогает межпроцедурный анализ. В случае его использования, анализ потока данных становится глобальным, т.е. анализируется перенос свойств объектов не только на уровне конкретной обрабатываемой компилятором процедуры, но и внутри всей программы.
Inter-procedural analysis и whole-program analysis — очень разные вещи. Первая в компиляторах реализуется относительно просто, а вторая — очень сложно.
> Векторизуется ли vector
std::valarray же. Именно поэтому его в стандарт добавили.
А вообще с моей точки зрения статья содержит набор малосвязанных фактов. То, что касается вводной части про архитектуру и так уже сто раз везде написано, а что касается внутренностей компилятора — вы написали в основном только названия оптимизаций (иногда довольно спорные переводы их названий на русский), которые неспециалисту ни о чём не говорят.
Ну или на Википедии, если стандарт — слишком сложно. en.wikipedia.org/wiki/C99#IEEE.C2.A0754_floating_point_support
Прежде, чем делать такие заявления как в первом сообщении, сделали бы хоты бы минимальный поиск или заглянули в стандарт, если не знаете. А то FUD.
сегментами -> секциями.
Нет. Это в исполняемом файле есть секции кода и данных. Они мапятся в плоское адресное пространство. Плоское (flat) адресное пространство плоское потому что у нас всего один *сегмент*, на который указывают все сегментные регистры (cs, ds, ss). Поэтому для интерпретации адреса не нужно знать из какого он сегмента, потому что сегмент один и тот же.
Но кроме исполняемого файла есть, например, библиотеки со своими сегментами. И всё это посегментно мапится в адресное пространство. При этом сегменты кода, естественно, не идут последовательно. И сегменты данных тоже. (см. опять таки /proc/PID/maps)
> У программы есть brk, который двигает end_data_segment.
Эмм… С точки зрения программиста — да, системный вызов brk что-то там двигает. На практике — он просто мапит/анмапит страницы в конец того что вы называете «сегментом данных».
brk [1] вызывает do_brk [2], который в результате создаёт anonymous mapping.
[1] lxr.linux.no/#linux+v3.6.6/mm/mmap.c#L246
[2] lxr.linux.no/#linux+v3.6.6/mm/mmap.c#L2161
Нет принципиальной разницы. В файловом маппинге backing store — файл, в анонимном — swap.
Есть страницы замапленные (cat /proc/self/maps) и незамапленные, обращение к которым ведёт к segfault.
> При вызове анонимного мапинга нужно передать свой указатель на доступный виртуальный адрес.
И передача этого адреса является только советом для mmap() и ничего не гарантирует. Этот адрес может быть не замаплен, но, например, ОС решила запретить его мапить (например, нулевой адрес — см. sysctl vm.mmap_min_addr) или процессор может не поддерживать его мапить (http://en.wikipedia.org/wiki/X86-64#Virtual_address_space_details).
> Чтобы адрес стал доступным, надо его разрешить — то есть поднять break point.
Нет единственного break point. Посмотрите /proc/self/maps у чего-нибудь посложнее, чем ls (например firefox). Страницы данных мапятся вплоть до гранулярности в 1 страницу.
> Насчёт sbrk — с ним могут работать несколько аллокаторов сразу, правда, dallocate при этом не получится, да.
Только если нет TOCTOU гонки между проверкой текущего break и установкой нового.
stdint.h
Теоретически — может. Компилятор может вызывать разные malloc() или malloc() может раскручивать стек. Конечно на практике, так никто делать не будет так как нет смысла.
> Память либо доступна в рамках всего процесса, либо нет, точка.
Вообще-то есть thread-aware аллокаторы.
> Однако при попытке выделить память из другого класса (либо статической функции) мы получим исключение — не достаточно памяти. По всей видимости при выделении памяти из статической функции память выделяется не в том же хипе, что и при обычном выделении изнутри класса приложения.
Также интересно что автор имел ввиду. Или его компилятор действительно вызывает разные malloc() в этих случаях (в чём я сомневаюсь), или написанное не соответствует наблюдениям.
ru.wikipedia.org/wiki/%D0%9F%D1%80%D0%B5%D0%B4%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE_%D0%B4%D0%BE%D0%BF%D1%83%D1%81%D1%82%D0%B8%D0%BC%D0%B0%D1%8F_%D0%BA%D0%BE%D0%BD%D1%86%D0%B5%D0%BD%D1%82%D1%80%D0%B0%D1%86%D0%B8%D1%8F
Вы бы сначала почитали как работают аллокаторы памяти прежде чем писать «разгромную» статью. Это же классическое поведение аллокатора: маленькие объекты выделяются в общей куче, а за большими — обращаемся к системному аллокатору.
Чем больше вы пишете, тем больше у меня впечатление что вы или вообще не в теме современных подходов к разработке компиляторов или всё намеренно делали в абсолютном информационном вакууме, согласно традиции вашей компании делать велосипеды.