Leif Gram: Mr. Fix

Пожелание

Требуется следующая оптимизация для C++.

Допустим, встрeтился такой код:
double x = drand48();
f(x, 1, true);


Допустим далее, что после перебора всех кандидатов для f, наилушей оказалась некоторая
f(double, int32_t, bool) {...},
причем ее определение известно.

В таком случае компилятор должен сгенерировать код определения функции одного переменного f_1(double), полученный из кода исходной функции f подстановкой констант на место остальных параметров, оптимизировать его согласно стандартным правилам оптимизации, и вызвать f_1(x).
А откуда известно, что он так не делает?
Не знаю, в стандарте-то нет, и никто таким не хвастается.
Но разве это уже не так? Если код функции известен at the call site, то компилятор попытается to expand этот код и все константы запустят элиминацию ненужного кода.
Это если он решить делать инлайнинг. Но ведь не все подряд функции инлайнятся, и делается это по каким-то нестандартным причинам, подозреваю, что связанными в первую очередь с длиной функции.
Я ни разу не эксперт по оптимизации, но насколько я знаю в большинстве компиляторов сейчас такие вещи строятся на heuristics. Т.е., другими словами: хрен поймёшь. Большинство стандартных функций инлайнятся - видимо, и потому что они таки да, короткие и код их известен, даже если не виден на момент вызова.

Более того, перезапуская компилятор, можно получить другой код! Потому что одна из метрик может быть время работы конкректного алгоритма оптимизации. Если считает слишком долго, то либо отменяется, либо принимаются результаты на момент принудительной остановки.
Ну вот, а то, что я говорю, может быть легко проделано на уровне препроцессинга текста программы стандартным и пресказуемым образом, а там уж пускай оптимизатор применяет свои эвристики. Неужели заранее очевидно, что это не нужно?
Думаю что это можно установить только экспериментально с конкретным компилятором. Сама по себе оптимизация хорошая, и я уверен что о ней уже думали и она срабатывает в каких-то случаях.
Это хорошо, но там, может, вся функция заинлайнена, для любых параметров, а потом оптимизирована поверх. На маленьком примере не будет видна разница.
Называется специализация или частичное вычисление (partial evaluation). Надо поискать, кто это делает и когда, если делает вообще :)
Как говорится, I'd like to raise the level of this discussion.

Давайте попросим constexpr функции с constexpr параметрами, то есть:

constexpr int f (double, constexpr int32_t, constexpr bool) { /* тело */ }

определяет потенциальное (так же, как и темплейты) семейство функций с частичными специализациями, от исходной 3-аргументной f до функции от одного double аргумента. Было, навскидку, предложение http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1045r0.html которое пробрасывает constexpr параметры, но это не совсем то.

В конкретно Вашем случае фиксируемы параметры целые, так что можно сделать темплейт функцию от одного аргумента double с такими темплейт параметрами и сделать специализации таким способом.

Тогда в коде будет

double x = drand48();
f<1, true>(x);

Сама f имплементируется по-прежнему, только вместо последних параметров функции используются параметры темплейта.

В более общем случае, можно применить clang-tools и напускать его на текст программы, заменяя все вызовы f(..., 1, true) на f_1_true(...) и для каждого такого случая копировать имплементацию f с подставленными константами. Как мотивирующий вариант, у нас есть процедура просмотра кода и убивание аргумента функции, если она всегда вызывается с конкретным значением этого аргумента - работает почти автоматизированно, если не напорется на хитрые имена функций.