16

SSEを使用して、ベクトル(u)を使用した4x4マトリックス(M)乗算の最も効率的な実装を見つけようとしています。つまり、Mu=vです。

私が理解している限り、これを行うには2つの主要な方法があります。

    method 1) v1 = dot(row1, u), v2 = dot(row2, u), v3 = dot(row3, u), v4 = dot(row4, u)
    method 2) v = u1 col1 + u2 col2 + u3 col3 + u4 col4.

方法2はSSE2で簡単に実装できます。方法1は、SSE3の水平加算命令またはSSE4の内積命令のいずれかを使用して実装できます。ただし、すべてのテストで、方法2は常に方法1よりも優れています。

方法1が有利な場所の1つは、たとえばアフィン変換の場合の3x4行列です。この場合、最後の内積は不要です。ただし、この場合でも、4x4マトリックスのメソッド2は、3x4マトリックスのメソッド1よりも高速です。4x4マトリックスのメソッド2よりも高速であることがわかった唯一のメソッドは、4x3マトリックスのメソッド2です。

では、水平加算と内積命令のポイントは何ですか?実際、この場合、ドット生成命令は最悪のパフォーマンスを示します。多分それはデータのフォーマットと関係がありますか?マトリックスの順序を定義できない場合は、転置が必要です。その場合は、方法1の方が適しているでしょうか。

いくつかのコードについては、以下を参照してください。

__m128 m4x4v_colSSE(const __m128 cols[4], const __m128 v) {
  __m128 u1 = _mm_shuffle_ps(v,v, _MM_SHUFFLE(0,0,0,0));
  __m128 u2 = _mm_shuffle_ps(v,v, _MM_SHUFFLE(1,1,1,1));
  __m128 u3 = _mm_shuffle_ps(v,v, _MM_SHUFFLE(2,2,2,2));
  __m128 u4 = _mm_shuffle_ps(v,v, _MM_SHUFFLE(3,3,3,3));

  __m128 prod1 = _mm_mul_ps(u1, cols[0]);
  __m128 prod2 = _mm_mul_ps(u2, cols[1]);
  __m128 prod3 = _mm_mul_ps(u3, cols[2]);
  __m128 prod4 = _mm_mul_ps(u4, cols[3]);

  return _mm_add_ps(_mm_add_ps(prod1, prod2), _mm_add_ps(prod3, prod4));
}

__m128 m4x4v_rowSSE3(const __m128 rows[4], const __m128 v) {
  __m128 prod1 = _mm_mul_ps(rows[0], v);
  __m128 prod2 = _mm_mul_ps(rows[1], v);
  __m128 prod3 = _mm_mul_ps(rows[2], v);
  __m128 prod4 = _mm_mul_ps(rows[3], v);

  return _mm_hadd_ps(_mm_hadd_ps(prod1, prod2), _mm_hadd_ps(prod3, prod4));
}

__m128 m4x4v_rowSSE4(const __m128 rows[4], const __m128 v) {
  __m128 prod1 = _mm_dp_ps (rows[0], v, 0xFF);
  __m128 prod2 = _mm_dp_ps (rows[1], v, 0xFF);
  __m128 prod3 = _mm_dp_ps (rows[2], v, 0xFF);
  __m128 prod4 = _mm_dp_ps (rows[3], v, 0xFF);

  return _mm_shuffle_ps(_mm_movelh_ps(prod1, prod2), _mm_movelh_ps(prod3, prod4),  _MM_SHUFFLE(2, 0, 2, 0));
}  
4

1 に答える 1

10

水平方向の内積命令は複雑です。これらは、単純な命令と同じようにプロセッサによって実行される複数のより単純なマイクロオペレーションに分解されます。水平加算およびドット積命令のマイクロオペレーションへの正確な分解はプロセッサー固有ですが、最近のIntelプロセッサーでは、水平加算は2 SHUFFLE + 1 ADDマイクロオペレーションに分解され、ドット積は1 MUL + 1 SHUFFLE +2ADDマイクロオペレーションに分解されます。多数のマイクロオペレーションに加えて、この命令はプロセッサパイプラインの命令デコーダにもストレスを与えます。Intelプロセッサは、サイクルごとに1つのそのような複雑な命令しかデコードできません(4つの単純な命令と比較して)。AMD Bulldozerでは、これらの複雑な命令の相対的なコストはさらに高くなります。

于 2013-02-20T02:50:09.067 に答える