Я часто нахожу себя пишущим очень похожий код для одной, двух и трехмерных версий данной операции / алгоритма. Поддержание всех этих версий может стать утомительным. Простая генерация кода работает довольно хорошо, но кажется, что должен быть лучший способ.
Есть ли относительно простой способ написать операцию один раз и сделать ее обобщенной для более высоких или более низких измерений?
Один конкретный пример: предположим, мне нужно вычислить градиент поля скорости в спектральном пространстве. В трех измерениях петли Фортрана будут выглядеть примерно так:
do k = 1, n
do j = 1, n
do i = 1, n
phi(i,j,k) = ddx(i)*u(i,j,k) + ddx(j)*v(i,j,k) + ddx(k)*w(i,j,k)
end do
end do
end do
где ddx
массив правильно определен. (Можно также сделать это с умножением матрицы.) Код для двумерного потока почти точно такой же, за исключением: третье измерение отбрасывается из циклов, индексов и количества компонентов. Есть ли лучший способ выразить это?
Другой пример: предположим, у меня есть скорости жидкости, определенные точечно на трехмерной сетке. Чтобы интерполировать скорость в произвольное место (т. Е. Не соответствующее точкам сетки), можно использовать одномерный алгоритм Невилла последовательно по всем трем измерениям (т. Е. Уменьшению размеров). Есть ли простой способ сделать размерную редукцию при одномерной реализации простого алгоритма?
источник
Вопрос подчеркивает, что большинство «простых» языков программирования (по крайней мере, C, Fortran) не позволяют вам делать это чисто. Дополнительным ограничением является то, что вы хотите удобство записи и хорошую производительность.
Поэтому вместо написания кода, специфичного для измерения, подумайте о написании кода, который генерирует специфичный для измерения. Этот генератор не зависит от размеров, даже если вычислительный код - нет. Другими словами, вы добавляете слой рассуждений между вашей нотацией и кодом, выражающим вычисления. Шаблоны C ++ означают одно и то же: с другой стороны, они встроены прямо в язык. Оборотная сторона, они несколько громоздки для написания. Это сводит вопрос к тому, как практически реализовать генератор кода.
OpenCL позволяет вам делать генерацию кода во время выполнения довольно чисто. Это также обеспечивает очень четкое разделение между «внешней управляющей программой» и «внутренними циклами / ядрами». Внешняя генерирующая программа гораздо менее ограничена в производительности и поэтому может быть написана на удобном языке, таком как Python. Это моя надежда на то, как PyOpenCL будет использоваться - извините за обновленный бесстыдный плагин.
источник
Это может быть выполнено на любом языке с помощью следующего грубого умственного прототипа:
Оттуда, это вопрос борьбы с синтаксисом вашего определенного языка, чтобы сохранить ваш код nd-совместимым.
Написав n-мерный решатель гидродинамики , я обнаружил, что полезно иметь язык, который поддерживает распаковку списка, подобного объекту, в качестве аргументов функции. Т.е. a = (1,2,3) f (a *) -> f (1,2,3). Дополнительно продвинутые итераторы (такие как ndenumerate в numpy) делают код на порядок чище.
источник
источник
Четкие ответы, если вы хотите сохранить скорость Fortran, - это использовать язык с надлежащей генерацией кода, такой как Julia или C ++. Шаблоны C ++ уже упоминались, поэтому я упомяну инструменты Джулии здесь. Сгенерированные функции Джулии позволяют вам использовать ее метапрограммирование для создания функций по запросу через информацию о типе. Так что, по сути, вы можете сделать здесь
а затем вы используете
N
программно для сборки кода, который вы хотите выполнить, учитывая егоN
размерность. Затем можно легко построить картезианскую библиотеку Джулии или пакеты, такие как выражения Einsum.jl, дляN
размерной функции.Что хорошо в Джулии, так это то, что эта функция статически компилируется и оптимизируется для каждого используемого вами нового размерного массива, поэтому она не будет компилировать больше, чем вам нужно, но при этом вы получите скорость C / Fortran. В конце концов, это похоже на использование шаблонов C ++, но это язык более высокого уровня с множеством инструментов, облегчающих его (достаточно просто, чтобы это было хорошей домашней работой для старшекурсника).
Другой язык, который хорош для этого, - Лисп, как Common Lisp. Он прост в использовании, поскольку, как и Julia, он предоставляет вам скомпилированный AST с большим количеством встроенных инструментов для самоанализа, но в отличие от Julia он не будет автоматически компилировать его (в большинстве дистрибутивов).
источник
Я в той же (Фортран) лодке. Когда у меня есть элементы 1D, 2D, 3D и 4D (я занимаюсь проективной геометрией), я создаю одинаковые операторы для каждого типа, а затем пишу свою логику с помощью уравнений высокого уровня, которые проясняют, что происходит. Это не так медленно, как вы могли бы подумать, чтобы иметь отдельные циклы каждой операции и много копий памяти. Я позволил компилятору / процессору выполнить оптимизацию.
Например
Быть использованным в уравнениях как
где
e
иr
иg
может иметь любую размерность, которая имеет математический смысл.источник