Мотивация

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

Написание симулятора уравнений Максвелла во время компиляции

В C ++ 11 есть спецификатор constexpr, который объявляет, что можно оценить значение функции или переменной во время компиляции. Такие переменные и функции затем могут использоваться там, где разрешены только выражения констант времени компиляции. Например, можно создать переменную constexpr и передать ее как параметр шаблона функции. В приведенном ниже коде определены переменные constexpr для двумерной сетки.

Уравнения Максвелла описывают эволюцию электрических и магнитных полей. Чтобы найти приближенное решение дифференциальных уравнений Максвелла, я использую метод конечных разностей во временной области или метод Йи. Значения электрического поля в каждой ячейке сохраняются для каждого шага отчета.

Центральная часть - это функция расчета временного шага. Для простоты моделирование содержит только один электромагнитный источник в центре сетки.

Здесь я использую гауссовский электромагнитный импульс. Как видите, он основан на вычислении экспоненты. И именно здесь я обнаружил некоторые интересные особенности программирования во время компиляции.

Ограничения времени компиляции

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

/usr/include/c++/7/cmath:223:26: error: ‘__builtin_expf(-8.85605545e+1f)’ is not a constant expression
   { return __builtin_expf(__x); }
            ~~~~~~~~~~~~~~^~~~~

Когда я получил эту ошибку, я попытался использовать источник гармоник, который основан на функции cos, но я получил ошибку того же типа. Чтобы справиться с этой проблемой, я использовал специальную реализацию времени компиляции. Хорошо то, что вы можете получить некоторую полезную информацию во время компиляции, например

/MetaMaxwell/main.cpp:189:15: note: floating point arithmetic produces a NaN
        dz[i] += C0 * dt * update_curl_h<float_type, nx, ny> (i, dx, dy, hx, hy);
              ^
/MetaMaxwell/main.cpp:233:7: note: in call to 'fdtd(t, 3.335641e-10, 2.000000e-01, 2.000000e-01, 2, er, mh, hx, hy, ez, dz)'
      fdtd<float_type, nx, ny> (t, dt, dx, dy, report_each, er, mh, hx, hy, ez, dz);
      ^
/MetaMaxwell/main.cpp:298:28: note: in call to 'collect_time_steps(2)'
  constexpr auto reports = collect_time_steps<double, nx, ny, 100> (2);

Кроме того, нет возможности доставлять сообщения, которые не прерывают процесс компиляции во время компиляции. Я нашел предложение для static_report, но оно еще не было одобрено.

Я также обнаружил проблему в компиляторах GCC. Я пробовал разные версии GCC (7.4, 9.2), и у обеих есть проблемы с памятью. Кажется, что GCC не освобождает память ни от одного вызова constexpr, потому что увеличение количества итераций значительно увеличивает потребление памяти. Процесс компиляции не умещается в моей 32гб памяти даже для небольших гридов (40х40). Напротив, потребление памяти clang не зависит от количества вызовов constexpr. Поэтому, если вы хотите проверить производительность вашего компилятора на больших сетках, вам следует попробовать clang.

Представление

Что ж, эта часть сообщения довольно разочаровывающая. Единственный хороший момент заключается в том, что время моделирования во время компиляции замедляется из-за количества вызовов, поэтому вы можете использовать double, не замечая падения производительности.

Другими словами, должно быть что-то вроде constexpr std :: thread.

Вывод

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