どうやって行列の列から4つの連続要素をNEONレジスタに格納するの?

C言語は行優先の並び順でデータを格納するため、行列の一行から連続する4つの要素をNEONレジスタに格納することは簡単です。

1
src1 = vld1q_f32(m_a[i] + k); // m_aの第i行のk番から始まる4つの連続した要素をsrc1に格納

それでは、行列m_bの第j列のk番から始まる4つの連続した要素をsrc2に格納したい場合はどうすればよいでしょうか?

もちろん、vsetq_lane_f32を使用することができます。

1
2
3
4
src2 = vsetq_lane_f32(m_b[k][j]src20);
src2 = vsetq_lane_f32(m_b[k + 1][j],src21);
src2 = vsetq_lane_f32(m_b[k + 2][j]src22);
src2 = vsetq_lane_f32(m_b[k + 3][j]src2,3);

1行で書くこともできます。

1
src2 = vsetq_lane_f32(m_b[k][j], vsetq_lane_f32(m_b[k + 1][j], vsetq_lane_f32(m_b[k + 2][j], vsetq_lane_f32(m_b[k + 3][j], accum, 3), 2), 1), 0);

しかし、これはとても不器用に見えます。すべての時間をアドレス指定に費やすようです。

実際に$1300$次正方行列の掛け算を行うと、この方法で〝最適化〟されたコードは、普通の3重ループに比べて遅くなります。

では、どのように書けばよいでしょうか?

$A\times B$では、列を取得するためにアドレス指定のコストがかかります。しかし、行だけを取得する必要がある場合は、そこまで時間がかかりません。したがって、$A\times B^{\mathrm T}$を計算することができます。$A$と$B$はどちらも、行の連続した要素だけを取得するだけで済みます。$A\times\left(B^{\mathrm T}\right)^{\mathrm T}=A\times B$を計算することは簡単です。最終的な解決策は、以下のようになります。

1
2
3
4
5
6
7
for(i = 0; i < N; i++){
    for(j = i + 1; j < N; j++){
        tmp = m_b[i][j];
        m_b[i][j] = m_b[j][i];
        m_b[j][i] = tmp;
    }
}