web-dev-qa-db-ja.com

AVX命令で水平ベクトル和を行う最速の方法

4つの64ビット浮動小数点値のパックされたベクトルがあります。
ベクトルの要素の合計を取得したいと思います。

SSE(および32ビットフロートを使用)を使用すると、次のことができます。

v_sum = _mm_hadd_ps(v_sum, v_sum);
v_sum = _mm_hadd_ps(v_sum, v_sum);

残念ながら、AVXは_mm256_hadd_pd命令を備えていますが、結果はSSEバージョンとは異なります。これは、ほとんどのAVX命令がSSE 128ビットの境界を越えることなく、128ビットの下位および上位ごとに個別に命令します。

理想的には、私が探しているソリューションは次のガイドラインに従う必要があります。
1)AVX/AVX2命令のみを使用します。 (SSEなし)
2)2〜3回以内の指示でそれを行います。

ただし、(上記のガイドラインに従わなくても)効率的でエレガントな方法は常に受け入れられています。

助けてくれてありがとう。

-ルイジカステッリ

16
Luigi Castelli

2つある場合__m256dベクトルx1およびx2それぞれに水平方向に合計したい4つのdoubleが含まれている場合は、次のようにします。

__m256d x1, x2;
// calculate 4 two-element horizontal sums:
// lower 64 bits contain x1[0] + x1[1]
// next 64 bits contain x2[0] + x2[1]
// next 64 bits contain x1[2] + x1[3]
// next 64 bits contain x2[2] + x2[3]
__m256d sum = _mm256_hadd_pd(x1, x2);
// extract upper 128 bits of result
__m128d sum_high = _mm256_extractf128_pd(sum1, 1);
// add upper 128 bits of sum to its lower 128 bits
__m128d result = _mm_add_pd(sum_high, _mm256_castpd256_pd128(sum));
// lower 64 bits of result contain the sum of x1[0], x1[1], x1[2], x1[3]
// upper 64 bits of result contain the sum of x2[0], x2[1], x2[2], x2[3]

したがって、3つの命令が必要な水平合計の2つを実行するように見えます。上記はテストされていませんが、概念を理解する必要があります。

23
Jason R

合計だけが必要で、少しのスカラーコードが受け入れられる場合:

__m256d x;
__m256d s = _mm256_hadd_pd(x,x);
return ((double*)&s)[0] + ((double*)&s)[2];
4
RJVB

次のように仮定すると、__m256d 4つのパックされたdoubleを含むベクトルで、そのコンポーネントの合計を計算したい場合、つまりa0, a1, a2, a3は、必要な各ダブルコンポーネントですa0 + a1 + a2 + a3次に、別のAVXソリューションを示します。

// goal to calculate a0 + a1 + a2 + a3
__m256d values = _mm256_set_pd(23211.24, -123.421, 1224.123, 413.231);

// assuming _mm256_hadd_pd(a, b) == a0 + a1, b0 + b1, a2 + a3, b2 + b3 (5 cycles) ...
values = _mm256_hadd_pd(values, _mm256_permute2f128_pd(values, values, 1));
// ^^^^^^^^^^^^^^^^^^^^ a0 + a1, a2 + a3, a2 + a3, a0 + a1

values = _mm256_hadd_pd(values, values);
// ^^^^^^^^^^^^^^^^^^^^ (a0 + a1 + a2 + a3), (a0 + a1 + a2 + a3), (a2 + a3 + a0 + a1), (a2 + a3 + a0 + a1)

// Being that addition is associative then each component of values contains the sum of all its initial components (11 cycles) to calculate, (1-2 cycles) to extract, total (12-13 cycles)
double got = _mm_cvtsd_f64(_mm256_castpd256_pd128(values)), exp = (23211.24 + -123.421 + 1224.123 + 413.231);

if (got != exp || _mm256_movemask_pd(_mm256_cmp_pd(values, _mm256_set1_pd(exp), _CMP_EQ_OS)) != 0b1111)
    printf("Failed to sum double components, exp: %f, got %f\n", exp, got);
else
    printf("ok\n");

このソリューションには、おそらく役立つ合計がブロードキャストされています...

問題を誤って解釈した場合は、お詫び申し上げます。

$ uname -a
Darwin Samys-MacBook-Pro.local 13.3.0 Darwin Kernel Version 13.3.0: Tue Jun  3 21:27:35 PDT 2014; root:xnu-2422.110.17~1/RELEASE_X86_64 x86_64

$ gcc --version
Configured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr --with-gxx-include-dir=/usr/include/c++/4.2.1
Apple LLVM version 5.1 (clang-503.0.40) (based on LLVM 3.4svn)
Target: x86_64-Apple-darwin13.3.0
Thread model: posix
3
Samy Vilar