Я сталкиваюсь с проблемой как можно быстрее вычислить произведения матрицы-вектора, где матрица строго вещественная, а вектор сложный. Чтобы быть более точным, реальная матрица является (анти-) симметричной c и имеет разреженную структуру ненулевых плотных блоков.
До сих пор моя стратегия состояла в том, чтобы разделить вектор на его реальный и мнимый разделить и вычислить для каждого плотного блока произведения матрицы-вектора как для вещественных, так и для мнимых частей. Поскольку матрица является (анти-) симметричной c, я намерен одновременно вычислять произведения для блока и его транспонирование, поэтому я могу просто повторно использовать строку кэша, в которой находится матрица. Поэтому для каждого блока я вычислить 4 матрично-векторных произведения для блока и его транспонирование для каждой действительной и мнимой части.
Мой код для вычисления этих 4 произведений для одного блока в конечном итоге выглядит следующим образом:
#define no_alias __restrict__
template <typename VecType, typename MatType>
void trans_mul( const VecType* const no_alias re_in,
VecType* const no_alias re_out,
const VecType* const no_alias im_in,
VecType* const no_alias im_out,
const VecType* const no_alias re_tin,
VecType* const no_alias re_tout,
const VecType* const no_alias im_tin,
VecType* const no_alias im_tout,
const MatType* no_alias mat, // the matrix block
const int rows,
const int cols)
{
for(int j = 0; j < cols; ++j) {
for(int i = 0; i < rows; ++i) {
const auto m = *mat++; // this is mat[i, j]
re_tout[j] += m * re_tin[i]; // transposed
im_tout[j] += m * im_tin[i]; // transposed
re_out[i] -= m * re_in[j];
im_out[i] -= m * im_in[j];
}
}
}
Типичный размер матрицы порядка 10 ^ 2. Я компилирую свой код, используя G CC 9.2.1 с -Ofast -march=native
. Из вывода сборки я вижу, что компилятор автоматически векторизуется и использует инструкции SIMD.
Я конкурирую с аналогичным кодом, написанным на Fortran, который все еще работает примерно на 25% быстрее , Конечно, мой код чрезвычайно наивен, но все же я не смог придумать ничего более быстрого, так как агрессивная оптимизация кажется очень эффективной. Я также пытался использовать четыре cblas_dgemv
звонка, что, однако, было значительно медленнее, чем мой наивный подход. Есть ли что-то еще, что я могу сделать, или есть какая-нибудь полезная процедура BLAS, которая могла бы удовлетворить мой случай?