Кастомные прекомпиляторы
От: Khimik  
Дата: 23.11.18 10:51
Оценка: :)
Интересно, не пробует ли кто-то из авторов ЯП делать кастомные прекомпиляторы, которые может написать программист-юзер?
Я имею в виду следующее (на примере Delphi). Предположим, я пишу библиотеку или exe-файл, которому на вход подаётся Delphi-юнит (паскалевский код), и моя программа его немного модифицирует. Среда разработки показывает в двух вкладках два кода: исходный юнит и юнит, модифицированный моим прекомпилятором. При запуске программы компилируется второй код, и естественно отладчик имеет дело с ним.
Вот пример. Есть простой приём оптимизации циклов – размножение. Пусть у меня есть код:

qfor6 i:= 0 to count-1 do
  values[i] := values[i]* values[i];


Мой прекомпилятор видит qfor6 и преобразует этот код таким образом:

Intervalscount := count div 6;
itersininervalscount:=intervalscount*6;
curiter:=0;
repeat
  values[curiter] := values[curiter]*values[curiter];
  inc(curiter);
  values[curiter] := values[curiter]*values[curiter];
  inc(curiter);
  values[curiter] := values[curiter]*values[curiter];
  inc(curiter);
  values[curiter] := values[curiter]*values[curiter];
  inc(curiter);
  values[curiter] := values[curiter]*values[curiter];
  inc(curiter);
  values[curiter] := values[curiter]*values[curiter];
  inc(curiter);
until curiter>=itersininervalscount;

for i:=itersininervalscount to count-1 do
  values[i] := values[i]* values[i];

(По ходу вопрос – насколько можно ещё оптимизировать этот код, или это уже предел?)
Ещё можно добавить вторую опцию для прекомпилятора: превратить qfor6 в обычный for. Это нужно для отладки, чтобы проще было разбираться с кодом.
У таких прекомпиляторов нашлось бы масса применений; каждый программист стал бы писать свой для своих задач, по сути совершенствуя исходный ЯП. И на rsdn все бы выкладывали свои прекомпиляторы в целях фаллометрии)
Вот ещё пример применения таких прекомпиляторов: инлайновый функции. Я попробовал освоить инлайновые функции в Delphi XE 8, и обнаружил что они совершенно неудобные: инлайновая функция не может обратиться к переменной в процедуре уровнем выше, или наоборот вызвать собственную вложенную функцию (уровнем ниже).
Мне кажется, написать продвинутый прекомпилятор – достаточно простая задача, по крайней мере я бы написал этот qfor6 за пару дней, и правильные инлайновые функции ещё за пару месяцев.
"Ты должен сделать добро из зла, потому что его больше не из чего сделать". АБ Стругацкие.
Re: Кастомные прекомпиляторы
От: sambl74 Россия  
Дата: 23.11.18 11:22
Оценка:
Здравствуйте, Khimik, Вы писали:

K>Интересно, не пробует ли кто-то из авторов ЯП делать кастомные прекомпиляторы, которые может написать программист-юзер?


Ну вообще что-то похожее темплейтами реализуется — когда у тебя есть темлейт, который введённый qfor6 разворачивает.
Re: Кастомные прекомпиляторы
От: LuciferSaratov Россия  
Дата: 23.11.18 11:49
Оценка:
Здравствуйте, Khimik, Вы писали:

K>Интересно, не пробует ли кто-то из авторов ЯП делать кастомные прекомпиляторы, которые может написать программист-юзер?


есть например макропроцессор m4, попробуй его посмотреть
Re: Кастомные прекомпиляторы
От: swame  
Дата: 23.11.18 11:51
Оценка:
Здравствуйте, Khimik, Вы писали:

K>Интересно, не пробует ли кто-то из авторов ЯП делать кастомные прекомпиляторы, которые может написать программист-юзер?

K>Я имею в виду следующее (на примере Delphi). Предположим, я пишу библиотеку или exe-файл, которому на вход подаётся Delphi-юнит (паскалевский код), и моя программа его немного модифицирует. Среда разработки показывает в двух вкладках два кода: исходный юнит и юнит, модифицированный моим прекомпилятором. При запуске программы компилируется второй код, и естественно отладчик имеет дело с ним.
K>Вот пример. Есть простой приём оптимизации циклов – размножение. Пусть у меня есть код:

K>
K>qfor6 i:= 0 to count-1 do
K>  values[i] := values[i]* values[i];
K>


K>Мой прекомпилятор видит qfor6 и преобразует этот код таким образом:


Осталось сравнить оба варианта на реальном измерении скорости. ПОдозреваю, что вариант с обычным for работать быстрее, потому что будет оптимизирован встроенным оптимизатором.
Отредактировано 23.11.2018 11:53 swame . Предыдущая версия .
Re: Кастомные прекомпиляторы
От: eskimo82  
Дата: 23.11.18 12:45
Оценка:
K>Я имею в виду следующее (на примере Delphi). Предположим, я пишу библиотеку или exe-файл, которому на вход подаётся Delphi-юнит (паскалевский код), и моя программа его немного модифицирует.
Поздравляю! Ты переизобрел препроцессор.

В Паскале его конечно нет, но можно вполне использовать m4 — Я пробовал на JAVA и на документации к проекту (всякие README) остался доволен.
Re[2]: Кастомные прекомпиляторы
От: Khimik  
Дата: 23.11.18 12:50
Оценка:
K>>Интересно, не пробует ли кто-то из авторов ЯП делать кастомные прекомпиляторы, которые может написать программист-юзер?

S>Ну вообще что-то похожее темплейтами реализуется — когда у тебя есть темлейт, который введённый qfor6 разворачивает.


Templates? В Delphi XE они есть?
"Ты должен сделать добро из зла, потому что его больше не из чего сделать". АБ Стругацкие.
Re[2]: Кастомные прекомпиляторы
От: Khimik  
Дата: 23.11.18 12:52
Оценка:
K>>Интересно, не пробует ли кто-то из авторов ЯП делать кастомные прекомпиляторы, которые может написать программист-юзер?

LS>есть например макропроцессор m4, попробуй его посмотреть


Мне пока главное — к Delphi этот m4 подключить нельзя?
"Ты должен сделать добро из зла, потому что его больше не из чего сделать". АБ Стругацкие.
Re[2]: Кастомные прекомпиляторы
От: Khimik  
Дата: 23.11.18 12:54
Оценка:
S>Осталось сравнить оба варианта на реальном измерении скорости. ПОдозреваю, что вариант с обычным for работать быстрее, потому что будет оптимизирован встроенным оптимизатором.

А в самом деле, как компилятор Delphi XE8 поддерживает многоядерность/многопроцессорность?
"Ты должен сделать добро из зла, потому что его больше не из чего сделать". АБ Стругацкие.
Re: Кастомные прекомпиляторы
От: Alexander G Украина  
Дата: 23.11.18 13:03
Оценка:
Здравствуйте, Khimik, Вы писали:


K>(По ходу вопрос – насколько можно ещё оптимизировать этот код, или это уже предел?)


Вообще-то для операциях на векторах есть SIMD инструкции.
Хороший компилятор в состоянии их сгенерировать из такого года.
Когда всё же не может, можно явно реализовать через интринсики, например, здесь, возможно, была бы _mm_mul_epi32.

Да, цикл разворачивать хороший компилятор тоже умеет, это вовсе не сложно.
Сложнее вопрос, стоит ли это делать в конкретном случае.
Поэтому такая оптимизация делается компилятором не очень часто, только для совсем небольших циклов.

Ну то есть, я бы вернул всё как было с циклом, если бы был уверен в Delphi компиляторе.
Русский военный корабль идёт ко дну!
Re: Кастомные прекомпиляторы
От: scf  
Дата: 23.11.18 13:14
Оценка:
Здравствуйте, Khimik, Вы писали:

K>Интересно, не пробует ли кто-то из авторов ЯП делать кастомные прекомпиляторы, которые может написать программист-юзер?


В некоторых современных языках есть, называется "макросы" (macro). Представляет из себя функцию, которая получает собранный компилятором AST и преобразует его.
Качественных макросов на уровне исходного кода не встречал.
Re: Кастомные прекомпиляторы
От: Evgeny.Panasyuk Россия  
Дата: 24.11.18 03:37
Оценка:
Здравствуйте, Khimik, Вы писали:

K>Интересно, не пробует ли кто-то из авторов ЯП делать кастомные прекомпиляторы, которые может написать программист-юзер?

K>Я имею в виду следующее (на примере Delphi). Предположим, я пишу библиотеку или exe-файл, которому на вход подаётся Delphi-юнит (паскалевский код), и моя программа его немного модифицирует. Среда разработки показывает в двух вкладках два кода: исходный юнит и юнит, модифицированный моим прекомпилятором. При запуске программы компилируется второй код, и естественно отладчик имеет дело с ним.
K>Вот пример. Есть простой приём оптимизации циклов – размножение. Пусть у меня есть код:

K>
K>qfor6 i:= 0 to count-1 do
K>  values[i] := values[i]* values[i];
K>


Если брать конкретно этот пример, то unroll'инг без проблем реализуется без макросов через функции высших порядков:
transform(values, begin(values), [](const auto &x)
{
    return x*x;
});

Причём под капотом transform может делать не только unroll, но и например разбивку на множество потоков.
На C++, такая абстракция в виде ФВП будет бесплатной (при включённой оптимизации) — то есть результат будет идентичен ручной раскрутке.

Если же в общем, то как упомянули выше — в некоторых языках есть встроенные синтаксические макросы.

Но ты, как я понимаю, спрашиваешь про внешние к коду преобразования. Тут могут быть разные варианты, в зависимости от степени интеграции с языком.
При слабой интеграции — будет примитивное манипулирование строчками кода — обычный внешний препроцессор.
Примером более сильной интеграции является Clang ASTMatchers + Rewriter: мы описываем паттерн синтаксического дерева кода который нас интересует, получаем callback при нахождении соответствующего паттерна, и переделываем синтаксическое дерево как нам необходимо:
https://www.youtube.com/watch?v=mVbDzTM21BQ
https://github.com/eliben/llvm-clang-samples/blob/master/src_clang/matchers_rewriter.cpp

K>(По ходу вопрос – насколько можно ещё оптимизировать этот код, или это уже предел?)


Зависит от того что у тебя за типы значений, и в целом что за программа.
Если это линейная алгебра, то имеет смысл использовать более высокие абстрации типа векторов и матриц, и оперировать непосредственно ими, а не педалить каждый цикл вручную.
Нормальные библиотеки (например Eigen) умеют оптимизировать линейную алгебру на уровне деревьев выражений. Например выражение вида:
vector_a = 5.0*vector_b + vector_c + vector_d + 3.0*vector_e;

будет вычислено одним циклом (плюс векторизованно и параллелизованно, или вовсе через gppgu).
Отредактировано 24.11.2018 3:38 Evgeny.Panasyuk . Предыдущая версия .
Re[2]: Кастомные прекомпиляторы
От: Khimik  
Дата: 24.11.18 04:32
Оценка:
K>>
K>>qfor6 i:= 0 to count-1 do
K>>  values[i] := values[i]* values[i];
K>>


EP>Если брать конкретно этот пример, то unroll'инг без проблем реализуется без макросов через функции высших порядков:

EP>
EP>transform(values, begin(values), [](const auto &x)
EP>{
EP>    return x*x;
EP>});
EP>

EP>Причём под капотом transform может делать не только unroll, но и например разбивку на множество потоков.
EP>На C++, такая абстракция в виде ФВП будет бесплатной (при включённой оптимизации) — то есть результат будет идентичен ручной раскрутке.

Вроде в Delphi XE8 ничего такого нет? И обычный for этого не делает?

Я писал, что обнаружил что инлайновые функции в Delphi XE8 неудобные. Мне кажется тут дело в том, что при развёртывании кода (размножение цикла, инлайновые функции и т.п.) становится очень трудно сделать взаимодействие отладчика с работающим кодом.
Поэтому правильный ЯП, по-моему, должен показывать два кода: исходный и прекомпилированный, где всё уже развёрнуто. Естественно отладчик будет иметь дело со вторым.
"Ты должен сделать добро из зла, потому что его больше не из чего сделать". АБ Стругацкие.
Re[3]: Кастомные прекомпиляторы
От: Evgeny.Panasyuk Россия  
Дата: 24.11.18 04:51
Оценка:
Здравствуйте, Khimik, Вы писали:

EP>>Причём под капотом transform может делать не только unroll, но и например разбивку на множество потоков.

EP>>На C++, такая абстракция в виде ФВП будет бесплатной (при включённой оптимизации) — то есть результат будет идентичен ручной раскрутке.
K>Вроде в Delphi XE8 ничего такого нет? И обычный for этого не делает?

Вопрос как я понял был в общем, а Delphi здесь был лишь примером.
(про Delphi в целом — вообще не понимаю зачем его сейчас (2018 год) использовать, разве что legacy какое древнее. Уж тем более не понимаю зачем на нём пытаться оптимизировать раскрутку циклов. (сам пользовался им и Pascal'ем лет 15 назад))

K>Я писал, что обнаружил что инлайновые функции в Delphi XE8 неудобные. Мне кажется тут дело в том, что при развёртывании кода (размножение цикла, инлайновые функции и т.п.) становится очень трудно сделать взаимодействие отладчика с работающим кодом.

K>Поэтому правильный ЯП, по-моему, должен показывать два кода: исходный и прекомпилированный, где всё уже развёрнуто. Естественно отладчик будет иметь дело со вторым.

Если речь про пошаговую отладку — то обычно достаточно попросить отладчик пропускать определение исходники при step-into (например предикат для пути).
Но методологически этот самый step-into должен быть не первым средством отладки, а одним из последних рассматриваемых.
Re[4]: Кастомные прекомпиляторы
От: Khimik  
Дата: 24.11.18 06:09
Оценка:
EP>Вопрос как я понял был в общем, а Delphi здесь был лишь примером.
EP>(про Delphi в целом — вообще не понимаю зачем его сейчас (2018 год) использовать, разве что legacy какое древнее. Уж тем более не понимаю зачем на нём пытаться оптимизировать раскрутку циклов. (сам пользовался им и Pascal'ем лет 15 назад))

У меня сильное подозрение, что Delphi — очень хороший язык, хорошо подходящий для высокоскоростных приложений, который пал жертвой неправильной моды (что-то вроде фишеровского убегания в программировании).
"Ты должен сделать добро из зла, потому что его больше не из чего сделать". АБ Стругацкие.
Re[5]: Кастомные прекомпиляторы
От: Evgeny.Panasyuk Россия  
Дата: 24.11.18 06:25
Оценка:
Здравствуйте, Khimik, Вы писали:

EP>>Вопрос как я понял был в общем, а Delphi здесь был лишь примером.

EP>>(про Delphi в целом — вообще не понимаю зачем его сейчас (2018 год) использовать, разве что legacy какое древнее. Уж тем более не понимаю зачем на нём пытаться оптимизировать раскрутку циклов. (сам пользовался им и Pascal'ем лет 15 назад))
K>У меня сильное подозрение, что Delphi — очень хороший язык, хорошо подходящий для высокоскоростных приложений, который пал жертвой неправильной моды (что-то вроде фишеровского убегания в программировании).

Объективные факты в следующем:
— в Delphi как языке нет фич необходимых для высокоскоростных приложений, которые уже есть в других языках
— библиотеки структур данных и алгоритмов крайне скудные, опять же по сравнению с другими языками
— набор оптимизаций реализованных в компиляторе — также скудный
— пользовательская база мизерная, и IMO со временем только будет уменьшаться — нет ни потребности ни мотивации в улучшении предыдущих трёх пунктов


А твои подозрения на чём основываются?
Re[6]: Кастомные прекомпиляторы
От: Khimik  
Дата: 24.11.18 06:58
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>Объективные факты в следующем:

EP>- в Delphi как языке нет фич необходимых для высокоскоростных приложений, которые уже есть в других языках
EP>- библиотеки структур данных и алгоритмов крайне скудные, опять же по сравнению с другими языками
EP>- набор оптимизаций реализованных в компиляторе — также скудный
EP>- пользовательская база мизерная, и IMO со временем только будет уменьшаться — нет ни потребности ни мотивации в улучшении предыдущих трёх пунктов
EP>

EP>А твои подозрения на чём основываются?


Я сейчас переписываю на Delphi старый алгоритм, точнее оптимизирую его. Получилось быстрее раз в десять (цифра условная).
И вот я думаю, что, с одной стороны, C++ даёт код, скажем, в два раза более быстрый, чем Delphi; но гораздо больше значение имеет оптимизация самого алгоритма, например скорость разных алгоритмов сортировки для больших массивов может отличаться в миллион раз. А поскольку оптимизированный алгоритм обычно более сложный и багоносящий, то очень важна удобная среда разработки, позволяющая отлавливать эти баги и вообще писать сложный структурированный код.
"Ты должен сделать добро из зла, потому что его больше не из чего сделать". АБ Стругацкие.
Re[7]: Кастомные прекомпиляторы
От: Evgeny.Panasyuk Россия  
Дата: 24.11.18 07:08
Оценка:
Здравствуйте, Khimik, Вы писали:

K>Я сейчас переписываю на Delphi старый алгоритм, точнее оптимизирую его. Получилось быстрее раз в десять (цифра условная).

K>И вот я думаю, что, с одной стороны, C++ даёт код, скажем, в два раза более быстрый, чем Delphi;

Значение имеет итоговая скорость программы. Если программа в 2x-50x медленнее за счёт того что ты взял Delphi, то это плохая программа с точки зрения "высокоскоростного кода" который мы обсуждаем.

K>но гораздо больше значение имеет оптимизация самого алгоритма,


Которая также без проблем делается и на C++

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


Баги в алгоритмах лучше всего отлавливаются чёткими компонентами и тестами, а не средой разработки. Готовых алгоритмических библиотек для C++ больше, они лучшего качества.
Но даже если допустить что среда решает — то вот прямо в той же среде где крутится Delphi, доступен и C++ (раньше назывался C++ Builder, или его уже выпилили?), не говоря уже о том что есть C++ IDE и от других производителей.
Re[8]: Кастомные прекомпиляторы
От: Khimik  
Дата: 24.11.18 07:47
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>Баги в алгоритмах лучше всего отлавливаются чёткими компонентами и тестами, а не средой разработки. Готовых алгоритмических библиотек для C++ больше, они лучшего качества.


Тогда возникает вопрос: какие бывают компоненты и тесты для отладки? Как их можно классифицировать? Мне проще самому такое написать (если я правильно понял что вы имеете в виду компоненты, написанные на самом ЯП для встраивания в код).
Если тема большая, я отдельно создам её в ФП.
"Ты должен сделать добро из зла, потому что его больше не из чего сделать". АБ Стругацкие.
Отредактировано 24.11.2018 7:48 Khimik . Предыдущая версия .
Re[8]: Кастомные прекомпиляторы
От: Khimik  
Дата: 24.11.18 07:57
Оценка:
EP>Значение имеет итоговая скорость программы. Если программа в 2x-50x медленнее за счёт того что ты взял Delphi, то это плохая программа с точки зрения "высокоскоростного кода" который мы обсуждаем.

Итоговая скорость определяется скоростью некоторых ключевых (лимитирующих) участков, только их и надо оптимизировать (возможно, переводить на ассемблер).
"Ты должен сделать добро из зла, потому что его больше не из чего сделать". АБ Стругацкие.
Re[9]: Кастомные прекомпиляторы
От: Evgeny.Panasyuk Россия  
Дата: 24.11.18 08:20
Оценка:
Здравствуйте, Khimik, Вы писали:

EP>>Баги в алгоритмах лучше всего отлавливаются чёткими компонентами и тестами, а не средой разработки. Готовых алгоритмических библиотек для C++ больше, они лучшего качества.

K>Тогда возникает вопрос: какие бывают компоненты и тесты для отладки?
K>Как их можно классифицировать? Мне проще самому такое написать (если я правильно понял что вы имеете в виду компоненты, написанные на самом ЯП для встраивания в код).

Под компонентами я имею в виду разбиение задач на по-возможности ортогональные части, которые легко протестировать по-отдельности.
Например при реализации quicksort, можно выделить под-алгоритм partition — который легко про-тестировать отдельно, который к тому же ещё и полезен сам по себе. Плюс компоненты могут быть готовыми — например в стандартной библиотеки C++ уже есть std::partition.

Под тестами понимаю обычные unit и интеграционные тесты — если ты покрыл все краевые случаи алгоритма partition тестами — то на этот компонент можно полагаться при разработки других компонентов, и не тратить каждый раз время на перепроверку кода partition.

K>Если тема большая, я отдельно создам её в ФП.


Тема действительно уже отошла от изначальной.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.