Памятка для функций времени компиляции

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

std.functional.memoize не работает КТ, так что об этом не может быть и речи. DMD и LDC не достаточно умны, чтобы запоминать чистые функции, по крайней мере, это результат моих экспериментов с простыми чистыми функциями: способ, которым я проверял, кэширует ли он результаты:

Используя простые аргументы:

int sumN(int n) pure {
    int result = 0;
    for (int i = 0; i<=n; i++) result += i;
    return result;
}

void main() {
    enum sumMany =    sumN(2000000);
    enum sumMany2 =   sumN(2000000);
    writeln(sumMany, " ", sumMany2);
}

Используя аргументы шаблона:

int sumN(int n)() pure {
    int result = 0;
    for (int i = 0; i<=n; i++) result += i;
    return result;
}

void main() {
    enum sumMany =    sumN!2000000;
    enum sumMany2 =   sumN!2000000;
    writeln(sumMany, " ", sumMany2);
}

Время компиляции (вызов sumN один раз против двух):

time dmd -O -release -inline -boundscheck=off repeatpure.d

или же

time ldc2 -O5 -release -inline -boundscheck=off repeatpure.d

Время компиляции всегда вдвое больше, когда у меня есть оба перечисления в исходном коде.

Есть ли способ запомнить функции КТ?

1 ответ

Решение

Шаблоны с одинаковыми аргументами должны создаваться только один раз, поэтому вы должны сделать результат постоянным значением (в данном случае enum) внутри самого шаблона.

Функция аргумента шаблона будет создаваться только один раз, но она будет запускаться каждый раз, когда вы это делаете. Создайте вспомогательную функцию, которая запускается только один раз:

template sumN(int n) {
    int helper() {
        int result = 0;
        for (int i = 0; i<=n; i++) result += i;
        return result;
    }

    // this is run once per unique argument group
    enum sumN = helper();
}

void main() {
    // so both of these now reference the same constant
    enum sumMany =    sumN!2000000;
    enum sumMany2 =   sumN!2000000;
    writeln(sumMany, " ", sumMany2);
}

Это не совсем памятка, но она может быть достаточно хороша для того, что вам нужно - обратите внимание, что время компиляции такое же, если вы прокомментируете одну из этих сумм.

Другие вопросы по тегам