为什么__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在发布模式下编译-O2优化已打开。
快速版本在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);
}
- 删除一个线程上有数百万个字符串的大型哈希映射会影响另一个线程的性能
- OpenMP阵列性能较差
- 递归列出所有目录中的C++与Python与Ruby的性能
- 大小相等但成员数量不同的结构之间的性能差异
- 为什么constexpr的性能比正常表达式差
- 在类中使用随机生成器时出现性能问题
- 在main()之外初始化std::vector会导致性能下降(多线程)
- 海湾合作委员会 ARM 性能下降
- GCC 和 Clang 代码性能的巨大差异
- 在容量内调整矢量大小时的性能影响
- 了解算法的性能差异(如果以不同的编程语言实现)
- 未达到的情况会影响开关外壳性能
- QStringList vs list<shared_ptr<QString>> 性能比较C++
- 是否总是可以将使用递归编写的程序重写为不使用递归的程序C++,性能观点是什么?
- 哪种方法更好,性能明智
- C++ 特征库:引用的性能开销<>
- 与多个 for 循环与单个 for 循环 wrt 相关的性能从多映射获取数据
- 基于范围的 for 循环range_declaration中各种说明符之间的性能差异
- std::p mr::memory_resource 如何与 std::container 产生性能差异?
- 为什么__m256而不是'float'提供超过 x8 的性能?