Почему компиляция D занимает так много времени?

D - один из самых быстрых языков программирования для компиляции, если не самый быстрый, но это не всегда так. Когда unittest включается, все становится очень медленным. Мой текущий проект имеет 6-7 модулей (~ 2000 LOC), причем каждый из них имеет unittests, который также содержит контрольные показатели. Вот некоторые цифры из моего текущего проекта:

dmd -O -noboundscheck принимает 0m1.287s

dmd -O -release -noboundscheck принимает 0m1.382s

dmd -O -inline -noboundscheck принимает 0m1.499s

dmd -O -inline -release -noboundscheck принимает 0m3.477s

добавление -unittest к любому из вышеперечисленных действий приведет к резкому увеличению времени компиляции:

dmd -O -inline -release -noboundscheck -unittest принимает 0m21.918s

и иногда он сбрасывает DMD:

time dmd -O t1.d -inline -noboundscheck -version=Double -unittest принимает 0m2.297s Internal error: ../ztc/gdag.c 776

Очевидно, unittest является ошибкой, но в то же время он стал важной частью моего проекта. Я хотел бы знать, нормальное ли замедление или это то, над чем работали? Мой проект растет, и с каждым новым unittest компиляция занимает больше времени и дольше. Единственное решение, которое я знаю, - отключить -release и -inline, но это не всегда желательно.

Ответ 1

У DMD есть известная проблема с оптимизацией: длинные блоки кода оптимизируются с помощью алгоритма O (n ^ 2), поэтому длинные функции долгое время для компиляции с оптимизацией.

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

void foo()
{
    // lots of code
    // more code
}

Включите это:

void foo()
{
    void block1()
    {
        // lots of code
    }
    block1();

    void block2()
    {
        // more code
    }
    block2();
}

Это сработало для меня.

Ответ 2

Очень небольшим улучшением производительности может быть перемещение экземпляра шаблона в область видимости модуля с помощью version(unittest) block, например:

auto foo(T)(T t) { return t; }

version(unittest) {
    alias foo!int fooInt;
}

unittest {
    auto x = fooInt(1);
}

Профилируя это, я оборачиваюсь с повышением скорости ~30msec, если я использую экземпляр шаблона aliased в 5000 эквивалентных блоках unittest через auto x = fooInt(1), по сравнению с его экземпляром непосредственно в каждом блоке unittest через auto x = foo(1) (это фактически расширяет до auto x = foo!int(1)).

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

Ответ 3

Я заменил большую часть моего общего кода, но сократил время компиляции на 4-5 секунд. Все ухудшилось, и я считаю, что компилятор, вероятно, проблема:

time dmd -O -inline -release -noboundscheck -unittest принимает 0m30.388s

time dmd -O -inline -release -noboundscheck принимает 0m11.597s

time dmd -inline -release -noboundscheck -unittest принимает 0m1.884s

Когда -O, -inline, -release и -unittest все установлены, компиляция занимает самое длинное. Отбрасывание -O резко сокращает время компиляции. Поэтому, чтобы сократить время компиляции во время unittesting, снимите флаг оптимизации. Для обычных компиляций вы можете использовать любой из трех (-inline, -release, -unittest) без проблем. По моему опыту, это комбинация всех трех, из-за чего компиляция занимает второе место и самая длинная, когда также устанавливается -unittest.