繁体   English   中英

为什么__m256而不是'float'会提供超过x8的性能?

[英]Why __m256 instead of 'float' gives more than x8 performance?

为什么我使用__m256数据类型获得如此巨大的加速(x16次)? 一次处理8个浮点数,所以我希望只看到x8加速?

我的CPU是4核Devil Canyon i7(有超线程)在发布模式下使用visual studio 2017进行编译 - 打开了优化。

快速版本在400x400矩阵上消耗0.000151秒:

//make this matrix only keep the signs of its entries
inline void to_signs() {

    __m256 *i = reinterpret_cast<__m256*>(_arrays);
    __m256 *end = reinterpret_cast<__m256*>(_arrays + arraysSize());

    __m256 maskPlus = _mm256_set1_ps(1.f);
    __m256 maskMin =  _mm256_set1_ps(-1.f);

    //process the main portion of the array.  NOTICE: size might not be divisible by 8:
    while(true){
        ++i;
        if(i > end){  break; }

        __m256 *prev_i = i-1;
        *prev_i = _mm256_min_ps(*prev_i, maskPlus);
        *prev_i = _mm256_max_ps(*prev_i, maskMin);
    }

    //process the few remaining numbers, at the end of the array:
    i--;
    for(float *j=(float*)i; j<_arrays+arraysSize(); ++j){
        //taken from here:http://www.musicdsp.org/showone.php?id=249
        // mask sign bit in f, set it in r if necessary:
        float r = 1.0f;
        (int&)r |= ((int&)(*j) & 0x80000000);//according to author, can end up either -1 or 1 if zero.
        *j = r;
    }
}

旧版本,运行时间为0.002416秒:

inline void to_signs_slow() {
    size_t size = arraysSize();

    for (size_t i = 0; i<size; ++i) {
        //taken from here:http://www.musicdsp.org/showone.php?id=249
        // mask sign bit in f, set it in r if necessary:

        float r = 1.0f;
        (int&)r |= ((int&)_arrays[i] & 0x80000000);//according to author, can end up either -1 or 1 if zero.
        _arrays[i] = r;
    }
}

它是秘密使用2个内核,所以一旦我开始使用多线程,这个好处就会消失吗?

编辑:

在较大的矩阵上,大小(10e6)x(4e4)我平均得到3和14秒。 所以仅仅是x4加速,甚至不是x8 这可能是由于内存带宽和不适合缓存的东西

不过,我的问题是关于令人愉快的x16加速惊喜:)

你的标量版本看起来很糟糕(使用类型 - 投影的参考投射),并且可能编译为非常低效的asm,这比将每个32位元素复制到1.0f的位模式慢很多。 这应该只取一个整数AND和一个OR来做它标量(如果MSVC无法为你自动矢量化),但如果编译器将它复制到XMM寄存器或其他东西,我不会感到惊讶。


您的第一个手动矢量化版本甚至不会执行相同的工作,但它只是将所有非符号位屏蔽掉以留下-0.0f+0.0f 因此它将编译为一个vandps ymm0, ymm7, [rdi]和一个带有vmovups [rdi], ymm0 SIMD存储,加上一些循环开销。

并不是说使用set1(1.0f)添加_mm256_or_ps会降低任何速度,你仍然会遇到缓存带宽或每时钟1个存储吞吐量的瓶颈。


然后,您将其编辑为一个版本,该版本夹在-1.0f .. +1.0f范围内,保留大小小于1.0的输入未修改。 这不会比两个按位运算慢,除了Haswell(魔鬼的峡谷)只在端口5上运行FP布尔值,而不是端口0或端口1上的实际FP填充。

特别是如果你没有用你的浮点数做任何其他事情,你实际上想要使用_si256内在函数只使用AVX2整数指令,以便更快地使用Haswell。 (但是如果没有AVX2,你的代码就无法运行。)

在Skylake和更新版本上,FP布尔值可以使用所有3个矢量ALU端口。 https://agner.org/optimize/用于指令表和uarch指南。)

您的代码应该类似于:

// outside the loop if you want
const __m256i ones = _mm256_castps_si256(_mm256_set1_ps(1.0f));

for (something ; p += whatever) {
    __m256i floats = _mm256_load_si256( (const __m256i*)p );
    __m256i signs = _mm256_and_si256(floats,  _mm256_set1_epi32(0x80000000));
    __m256i applied = _mm256_or_si256(signs, ones);
    _mm256_store_si256((__m256i*)p, applied);

}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM