2012-02-13 16 views
4

2つのベクトルがそれぞれサイズ2のdoubleの2つの配列で表されるとします。対応する位置を追加したいと思います。ベクトルi0i1と仮定して、i0[0] + i1[0]i0[1] + i1[1]を一緒に追加したいと思います。SSEの読み込みと追加

タイプがdoubleなので、2つのレジスタが必要です。トリックはi0[0]i1[0]i0[1]i1[1]を別のものに入れて、それ自体でレジスタを追加するだけです。

私の質問私は_mm_load_ps(i0[0])を呼び出してから、_mm_load_ps(i1[0])あれば、それは別に下位および上位64ビットにそれらを配置しますされ、または、第2 loadをレジスタに置き換えられますか?どのようにして同じレジスターに両方のダブルスを置くので、add_psの後に電話することができますか?

おかげで、

答えて

7

私が何をしたいことは、このだと思う:

double i0[2]; 
double i1[2]; 

__m128d x1 = _mm_load_pd(i0); 
__m128d x2 = _mm_load_pd(i1); 
__m128d sum = _mm_add_pd(x1, x2); 
// do whatever you want to with "sum" now 

あなたが_mm_load_pdを行うと、それはレジスタの下位64ビット、上位への第二に最初の二重を置きます16ビット。したがって、上記の負荷の後で、x1は、とi0[1]の2つの値(x2と同様)を保持します。 _mm_add_pdを呼び出すと、x1x2に対応する要素が垂直方向に追加されるため、加算後、sumは、下位64ビットにi0[0] + i1[0]、上位64ビットにi0[1] + i1[1]を保持します。

編集:_mm_load_psの代わりに_mm_load_pdを使用する利点はないことを指摘しておきます。関数名が示しているように、pdのバラエティは2つのパックド・ダブルを明示的にロードし、psバージョンは4つのパックド単精度浮動小数点をロードします。これらは純粋にビット単位のメモリ移動であり、どちらもSSE浮動小数点ユニットを使用するため、_mm_load_psを使用してdoubleデータにロードするとペナルティは発生しません。そして、_mm_load_psにはメリットがあります:その命令エンコーディングは_mm_load_pdより1バイト短く、命令キャッシュの意味では効率的です(そして、命令デコードの可能性もありますが、現代のx86プロセッサの複雑さの熟練者ではありません)。以下のような_mm_load_psを使用して上記のコードでは、なりますキャストによる暗黙の機能がない

double i0[2]; 
double i1[2]; 

__m128d x1 = (__m128d) _mm_load_ps((float *) i0); 
__m128d x2 = (__m128d) _mm_load_ps((float *) i1); 
__m128d sum = _mm_add_pd(x1, x2); 
// do whatever you want to with "sum" now 

。 SSEレジスタの内容を浮動小数点の代わりにdoubleを保持するようにコンパイラに再解釈させるだけで、倍精度算術関数_mm_add_pdに渡すことができます。

+0

あなたは確かに '_mm_load_ps'を使用することができますが、シングルの間でドメイン・バイパス・ペナルティがあるような方法で設計された仮想的な将来のプロセッサ上のパフォーマンスの低下を危険にさらします倍精度浮動小数点演算をサポートしています。私はそのようなプロセッサーの計画がないことを知っていますが、それは今まで実装されていないと言っているわけではありません。そのため、個別のロード操作が行われます。確かに遠隔の可能性はありますが、なぜそれを危険にさらしますか? –

+0

今後のプロセッサでパフォーマンスが低下する危険性があることに同意します。私は、アプリケーション特有の基準で「MOVPD」の代わりに「MOVPS」を使用することによって得られる性能上の利益を考慮する(すなわち測定する)ことを提案する。今日、それを使用する利点があり、そのためのペナルティを持っているような幻想的なアーキテクチャの兆候がない場合、私はそれをやります。このようなロードは、将来自動的に別の実装に切り替えるために簡単に抽象化することができます。 –

3

接頭辞は、「packed single」の略語であり、倍精度ではなく単精度浮動小数点での使用を意味します。

代わりに、_mm_load_pd()が必要です。この関数は、2つの配列doubleの最初のメンバーに16バイトの位置合わせされたポインタをとり、両方をロードします。だから、そうのように、これを使用します。

__m128d v0 = _mm_load_pd(i0); 
__m128d v1 = _mm_load_pd(i1); 

v0 = _mm_add_pd(v0, v1); 
+0

実際には、_mm_load_psは倍精度値で使用できます(そうすることで利点があります)。私の答えを見てください。 –