6

次の NEON 最適化関数を検討してください。

void mat44_multiply_neon(float32x4x4_t& result, const float32x4x4_t& a, const float32x4x4_t& b) {
    // Make sure "a" is mapped to registers in the d0-d15 range,
    // as requested by NEON multiply operations below:
    register float32x4_t a0 asm("q0") = a.val[0];
    register float32x4_t a1 asm("q1") = a.val[1];
    register float32x4_t a2 asm("q2") = a.val[2];
    register float32x4_t a3 asm("q3") = a.val[3];
    asm volatile (
    "\n\t# multiply two matrices...\n\t"
    "# result (%q0,%q1,%q2,%q3)  = first column of B (%q4) * first row of A (q0-q3)\n\t"
    "vmul.f32 %q0, %q4, %e8[0]\n\t"
    "vmul.f32 %q1, %q4, %e9[0]\n\t"
    "vmul.f32 %q2, %q4, %e10[0]\n\t"
    "vmul.f32 %q3, %q4, %e11[0]\n\t"
    "# result (%q0,%q1,%q2,%q3) += second column of B (%q5) * second row of A (q0-q3)\n\t"
    "vmla.f32 %q0, %q5, %e8[1]\n\t"
    "vmla.f32 %q1, %q5, %e9[1]\n\t"
    "vmla.f32 %q2, %q5, %e10[1]\n\t"
    "vmla.f32 %q3, %q5, %e11[1]\n\t"
    "# result (%q0,%q1,%q2,%q3) += third column of B (%q6) * third row of A (q0-q3)\n\t"
    "vmla.f32 %q0, %q6, %f8[0]\n\t"
    "vmla.f32 %q1, %q6, %f9[0]\n\t"
    "vmla.f32 %q2, %q6, %f10[0]\n\t"
    "vmla.f32 %q3, %q6, %f11[0]\n\t"
    "# result (%q0,%q1,%q2,%q3) += last column of B (%q7) * last row of A (q0-q3)\n\t"
    "vmla.f32 %q0, %q7, %f8[1]\n\t"
    "vmla.f32 %q1, %q7, %f9[1]\n\t"
    "vmla.f32 %q2, %q7, %f10[1]\n\t"
    "vmla.f32 %q3, %q7, %f11[1]\n\t\n\t"
    : "=&w"  (result.val[0]), "=&w"  (result.val[1]), "=&w"  (result.val[2]), "=&w" (result.val[3])
    : "w"   (b.val[0]),      "w"   (b.val[1]),      "w"   (b.val[2]),      "w"   (b.val[3]),
      "w"   (a0),            "w"   (a1),            "w"   (a2),            "w"   (a3)
    :
    );
}

最初のマトリックスをロードするために、GCC 4.5 がこの忌まわしきものを生成するのはなぜですか。

vldmia  r1, {d0-d1}
vldr    d2, [r1, #16]
vldr    d3, [r1, #24]
vldr    d4, [r1, #32]
vldr    d5, [r1, #40]
vldr    d6, [r1, #48]
vldr    d7, [r1, #56]

…代わりに:

vldmia  r1, {q0-q3}

…?

私が使用するオプション:

arm-none-eabi-gcc-4.5.1 -x c++ -march=armv7-a -mcpu=cortex-a8 -mfpu=neon -mfloat-abi=softfp -O3 -ffast-math -fgcse-las -funsafe-loop-optimizations -fsee -fomit-frame-pointer -fstrict-aliasing -ftree-vectorize

iPhoneOS が提供するコンパイラを使用しても同じ結果が得られることに注意してください。

/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/gcc-4.2 -x c++ -arch armv7 -mcpu=cortex-a8 -mfpu=neon -mfloat-abi=softfp -O3 -ffast-math -fgcse-las -funsafe-loop-optimizations -fsee -fomit-frame-pointer -fstrict-aliasing -ftree-vectorize
4

3 に答える 3

6

簡単な答え:

現在、GCC コンパイラは ARM コードの生成にあまり適していません。他のコードをよく見ると、関数プロローグ/エピローグやインライン memcpy などのハードコーディングされた場所を除いて、GCC が複数のレジスタのロード/ストアを使用できるレジスタを配置することはほとんどないことがわかります。

Neon 命令を使用すると、コードはさらに悪化します。これは、NEON ユニットの動作方法と関係があります。レジスタ ペアをクワッドまたはダブル ワードのいずれかとして扱うことができます。これは (私が知る限り) GCC がサポートするアーキテクチャー内でのレジスター使用のユニークな機能です。したがって、コード ジェネレーターは、すべてのインスタンスで最適なコードを生成しているわけではありません。

ところで:私がそうしている間:GCCは、Cortex-A8で「無料」のバレルシフター機能を使用すると、レジスタのスケジューリングに重要な影響を与えることを認識しておらず、GCCはほとんど間違っています。

于 2011-01-01T21:01:56.750 に答える
1

PPC には同様の命令 (ldmwおよびstmw) があり、一部のアーキテクチャでは、同等の一連のロード/ストアよりも実際に実行が遅くなります。明らかに、命令キャッシュ スペースやその他の考慮事項とのトレードオフになる可能性があります。gcc が本当に「間違っている」かどうかを確認するには、ターゲットの ARM プラットフォームでテストを行う必要があります。

于 2011-01-01T21:09:13.420 に答える
1

これはあなたが与えたスニペットには当てはまりませんが、実際の NEON コードでは vld1 を 128 ビットまたはおそらく 256 ビットのブロックに分割すると、コードのパフォーマンスが向上する可能性があります。これは、NEON のロードとストア (および置換) が他の NEON 命令と二重発行できるためですが、二重発行はマルチサイクル命令の最初または最後のサイクルでしか発生しません。アライメントされている場合、1 サイクルで 128 ビット、2 サイクルで 256 ビットのロードを取得できます。

于 2011-06-17T04:39:34.127 に答える