Я изучаю способы ускорения большой части кода C++, который имеет автоматические производные для вычисления якобианцев. Это включает в себя выполнение некоторой работы в фактических остатках, но большая часть работы (основанная на профилированном времени выполнения) заключается в вычислении якобианцев.
Это меня удивило, так как большинство якобианцев распространяются вперед от 0s и 1s, поэтому объем работы должен быть 2-4x, а не 10-12x. Чтобы смоделировать то, на что похоже большое количество работы в jacobian, я сделал супер минимальный пример с помощью только точечного продукта (вместо sin, cos, sqrt и более того, что было бы в реальной ситуации), что компилятор должен быть способен для оптимизации до одного возвращаемого значения:
#include <Eigen/Core>
#include <Eigen/Geometry>
using Array12d = Eigen::Matrix<double,12,1>;
double testReturnFirstDot(const Array12d& b)
{
Array12d a;
a.array() = 0.;
a(0) = 1.;
return a.dot(b);
}
Который должен быть таким же, как
double testReturnFirst(const Array12d& b)
{
return b(0);
}
Я был разочарован тем, что без ускоренной математики ни GCC 8.2, ни Clang 6, ни MSVC 19 не смогли сделать никаких оптимизаций на наивном dot-продукте с матрицей, полной 0s. Даже с быстрой математикой (https://godbolt.org/z/GvPXFy) оптимизация очень плоха в GCC и Clang (по-прежнему связаны с умножениями и дополнениями), и MSVC вообще не делает никаких оптимизаций.
У меня нет фона в компиляторах, но есть ли причина для этого? Я вполне уверен, что в значительной части научных вычислений, способных лучше обеспечивать постоянное распространение/сгибание, можно было бы сделать больше оптимизаций, даже если сама константная складка не привела к ускорению.
Хотя мне интересно объяснять, почему это не делается на стороне компилятора, мне также интересно, что я могу сделать на практической стороне, чтобы быстрее сделать свой собственный код, когда сталкиваюсь с такими типами шаблонов.