内在向量与朴素向量约简结果的差异

Ath*_*bey 1 c++ vector simd intrinsics ieee-754

我一直在比较 Intrinsics 向量缩减、朴素向量缩减和使用 openmp 编译指示的向量缩减的运行时间。然而,我发现这些场景的结果是不同的。代码如下 - (内在向量归约取自 - Fastest way to dohorizo​​ntal SSE vector sum(或其他归约)

#include <iostream>
#include <chrono>
#include <vector>
#include <numeric>
#include <algorithm>
#include <immintrin.h>


inline float hsum_ps_sse3(__m128 v) {
    __m128 shuf = _mm_movehdup_ps(v);        // broadcast elements 3,1 to 2,0
    __m128 sums = _mm_add_ps(v, shuf);
    shuf        = _mm_movehl_ps(shuf, sums); // high half -> low half
    sums        = _mm_add_ss(sums, shuf);
    return        _mm_cvtss_f32(sums);
}


float hsum256_ps_avx(__m256 v) {
    __m128 vlow  = _mm256_castps256_ps128(v);
    __m128 vhigh = _mm256_extractf128_ps(v, 1); // high 128
           vlow  = _mm_add_ps(vlow, vhigh);     // add the low 128
    return hsum_ps_sse3(vlow);         // and inline the sse3 version, which is optimal for AVX
    // (no wasted instructions, and all of them are the 4B minimum)
}

void reduceVector_Naive(std::vector<float> values){
    float result = 0;
    for(int i=0; i<int(1e8); i++){
        result  += values.at(i);
    }
    printf("Reduction Naive = %f \n", result);
}


void reduceVector_openmp(std::vector<float> values){
    float result = 0;
    #pragma omp simd reduction(+: result)
    for(int i=0; i<int(1e8); i++){
        result  += values.at(i);
    }

    printf("Reduction OpenMP = %f \n", result);
}

void reduceVector_intrinsics(std::vector<float> values){
    float result = 0;
    float* data_ptr = values.data();

    for(int i=0; i<1e8; i+=8){
        result  += hsum256_ps_avx(_mm256_loadu_ps(data_ptr + i));
    }

    printf("Reduction Intrinsics = %f \n", result);
}


int main(){

    std::vector<float> values;

    for(int i=0; i<1e8; i++){
        values.push_back(1);
    }


    reduceVector_Naive(values);
    reduceVector_openmp(values);
    reduceVector_intrinsics(values);

// The result should be 1e8 in each case
}
Run Code Online (Sandbox Code Playgroud)

但是,我的输出如下 -

Reduction Naive = 16777216.000000 
Reduction OpenMP = 16777216.000000 
Reduction Intrinsics = 100000000.000000 
Run Code Online (Sandbox Code Playgroud)

可以看出,只有内部函数能够正确计算,其他函数都面临精度问题。我完全意识到由于四舍五入而使用浮点可能会面临的精度问题,所以我的问题是,为什么内在函数得到正确的答案,即使它实际上也是浮点值算术。

我将其编译为 -g++ -mavx2 -march=native -O3 -fopenmp main.cpp
尝试使用版本7.5.0以及10.3.0

TIA

Ale*_*iev 6

Na\xc3\xafve 循环添加了1.0,并且在 处停止添加16777216.000000,因为 binary32 float 中没有足够的有效数字。

\n

请参阅此问答:为什么在 C# 中浮点变量在 16777216 处停止递增?

\n

当您添加计算的水平总和时,它将添加8.0,因此从停止添加开始的数字约为16777216*8= 134217728,您只是在实验中达不到它。

\n