web-dev-qa-db-ja.com

C / C ++のシンプルで高速な行列-ベクトル乗算

行列をベクトルで乗算するmatrix_vector_mult()を頻繁に使用する必要があります。以下がその実装です。

質問:大幅に、少なくとも2倍速くする簡単な方法はありますか?

備考:1)マトリックスのサイズは約300x50です。実行中は変化しません。 2)WindowsとLinuxの両方で動作する必要があります。

double vectors_dot_prod(const double *x, const double *y, int n)
{
    double res = 0.0;
    int i;
    for (i = 0; i < n; i++)
    {
        res += x[i] * y[i];
    }
    return res;
}

void matrix_vector_mult(const double **mat, const double *vec, double *result, int rows, int cols)
{ // in matrix form: result = mat * vec;
    int i;
    for (i = 0; i < rows; i++)
    {
        result[i] = vectors_dot_prod(mat[i], vec, cols);
    }
}
20
Serg

これは理論的には優れたコンパイラー自体が行うべきことですが、私のシステム(g ++ 4.6.3)で試してみたところ、300x50マトリックスで4倍の乗算を手動でアンロールすることで約2倍の速度が得られました(マトリックスごとに約18us)マトリックスあたり34us):

double vectors_dot_prod2(const double *x, const double *y, int n)
{
    double res = 0.0;
    int i = 0;
    for (; i <= n-4; i+=4)
    {
        res += (x[i] * y[i] +
                x[i+1] * y[i+1] +
                x[i+2] * y[i+2] +
                x[i+3] * y[i+3]);
    }
    for (; i < n; i++)
    {
        res += x[i] * y[i];
    }
    return res;
}

ただし、このレベルのマイクロ最適化の結果は、システム間で大きく異なることが予想されます。

22
6502

Zhenyaが言うように、優れたBLASまたは行列数学ライブラリを使用してください。

なんらかの理由でそれができない場合は、コンパイラがループを展開またはベクトル化できるかどうかを確認してください。 rowscolsが両方とも呼び出しサイトで定数であることを確認すると、投稿した関数がインライン化に使用できると想定すると役立つ場合があります

それでも必要なスピードアップが得られない場合は、手動で展開し、拡張機能またはインラインアセンブラを使用してベクトル化することを検討しています。

4
Useless

サイズが一定で事前にわかっている場合は、プリコンパイラー変数として渡します。これにより、コンパイラーはより完全に最適化できます。

0
djechlin