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

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

Теперь то же самое ТОЛЬКО для Delphi...

А какая разница, какой ЯП взяли? Тут просто об оптимизации рассказывается, где-то она лучше, где-то хуже. Просто пример дан для популярных компиляторов.

Компилятор может не только код отомтимизировать, но и память.

Я в своё время сражался с llvm, пытаясь убедить его оставить в экзешнике крупный массив байт (в готовый экзешник в этот массив записывалась дополнительная информация).

gcc в такой ситуации достаточно было массив инициализировать, хотя бы не полностью - а llvm отследил варианты предсказуемой инициализации массива (например, константой отличной от нуля) и массив из экзешника выпиливал. Создавал динамически, на этапе инициализации.

Пришлось инициализировать массив сложной последовательностью, так чтобы компилятор не догадался, как её можно сгенерировать.

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

Компилятор не удалял массив. Он сохранял его в исполняемом файле в компактном виде, в виде алгоритма, который генерировал его в оперативной памяти при старте программы.

а llvm отследил варианты предсказуемой инициализации массива (например, константой отличной от нуля) и массив из экзешника выпиливал.

Обращение к памяти можно запретить как-либо оптимизировать. Обратиться к массиву через указатель на volatile, например. И раз компилятору запрещено для оптимизаций "заглядывать внутрь" этого обращения, ему придётся оставить предыдущие шаги - сам массив в памяти и его инициализацию.
https://godbolt.org/z/59PfdPKsh

Потом из-за нужды в стандартном способе затирания паролей в C появился memset_explicit (но не в C++). По ссылке есть другие варианты решения помимо volatile.
https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2897.htm#:~:text=volatile%20pointer

> (1) что, (2) почему, (3) как.

Но мой опыт объяснений "что" - говорит, что без разъяснения аритектуры компиляторов хотя бы совсем-совсем по-верхам (плюс поверхностного объяснения некоторых моментов - типа неразрешимости задачи алиасов указателей в общем случае) не приносит пользы.
Т.е. не даёт метода понять "что же произойдёт"

Рад буду, если ошибаюсь в данном случае.
Работа большая и явно потребовавшая много времени.

В D можно просто самому выполнить функцию во время компиляции:

enum x = collatz(27);

Компилятор её скомпилирует (с оптимизациями), выполнит, и результат положит в константу. godbolt

  1. Пример с заменой умножения на сложение:

void init_scaled(int *array, int n, int scale) {
  for (int i = 0; i < n; i++) {
    array[i] = i * scale;
  }

void init_scaled(int *array, int n, int scale) {
  int tmp = 0;
  for (int i = 0; i < n; i++) {
    array[i] = tmp;
    tmp += scale;
  }
}

С одной стороны ускорили, а с другой - сделали итерации взаимозависимыми и зарубили SIMD. Как компилятор решает, что когда выгоднее?

  1. Вычисления на этапе компиляции и бесконечный цикл.

    Тут могут быть два поведения. Оба отталкиваются от рассуждения "в коде нет UB", а вот дальше варианты: "поэтому цикл надо выполнить" и "поэтому цикл можно безопасно удалить, если он не вычислился за заданное число циклов".

С одной стороны ускорили, а с другой - сделали итерации взаимозависимыми и зарубили SIMD.

Ничто не мешает компилятору после этого сделать анролл и векторизовать.

Как компилятор решает, что когда выгоднее?

Компиляторщики тюнят эвристики под определённый набор бенчмарков.

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации