使用结果浮点数时的 SSE SIMD 分段错误
Posted
技术标签:
【中文标题】使用结果浮点数时的 SSE SIMD 分段错误【英文标题】:SSE SIMD Segmentation Fault when using resulting float 【发布时间】:2016-11-16 19:45:53 【问题描述】:我正在尝试使用 Intel Intrinsics 在float
数组上快速执行操作。操作本身似乎运行良好。但是,当我尝试将操作结果放入标准 C 变量时,我得到一个 SEGFAULT。如果我将下面指示的行注释掉,程序就会运行。如果我保存了指示行的结果,但不以任何方式对其进行操作,则程序运行良好。只有当我尝试(以任何方式)与_mm_cvtss_f32(C)
的结果交互时,我的程序才会崩溃。有什么想法吗?
float proc(float *a, float *b, int n, int c, int width)
// Operation: SUM: (A - B) ^ 2
__m128 A, B, C;
float total = 0;
for (int d = 0, k = 0; k < c; d += width, k++)
for (int i = 0; i < n / 4 * 4; i += 4)
A = _mm_load_ps(&a[i + d]);
B = _mm_load_ps(&b[i + d]);
C = _mm_sub_ps(A, B);
C = _mm_mul_ps(C, C);
C = _mm_hadd_ps(C, C);
C = _mm_hadd_ps(C, C);
total += _mm_cvtss_f32(C); // SEGFAULT HERE
for (int i = n / 4 * 4; i < n; i++)
int diff = a[i + d] - b[i + d];
total += diff * diff;
return total;
【问题讨论】:
您确定您的程序实际上在您引用的指令处崩溃,或者如果您删除_mm_cvtss_f32()
行,编译器是否只是优化循环的其余部分(它没有任何其他可见的副作用)?潜在的失败原因是 a
和 b
数组的不正确对齐,因为您使用的是对齐的加载指令。你确定它们是 16 字节对齐的吗?在当代 Intel 硬件上,16 字节对齐和未对齐加载之间的性能差异非常小(movaps
的指令编码比 movups
短,但仅此而已)。
谢谢,我将load
更改为loadu
,现在似乎可以使用了!
@JasonR:它们的编码长度相同。 felixcloutier.com/x86/MOVAPS.html 与 felixcloutier.com/x86/MOVUPS.html。如果您要比较反汇编,其中一个是否具有 REX 前缀或不同的寻址模式?无论如何,当数据在运行时对齐时,它们的性能相同,但是当 L1 缓存读取带宽成为瓶颈时,对齐负载具有优势。确保您的数据在廉价时保持一致是个好主意。
另外,@Simon:将 HADD 指令从循环中取出,并在最后进行水平求和。使用_mm_add_ps
将结果向量添加到向量累加器中。 (最好使用多个向量累加器展开以隐藏添加到累加器的延迟,因此它不会成为循环携带依赖链延迟的瓶颈。自动向量化在这里可能会很好,尤其是如果你告诉编译器指针对齐。)
@PeterCordes:我绝对尊重您广泛的 x86 专业知识。我没有意识到现在对齐的负载还有任何剩余的优势。您对上面指出的 L1-cache-read-bandwidth 问题有参考吗?
【参考方案1】:
您确定您的程序实际上在您引用的指令处崩溃,或者如果您删除 _mm_cvtss_f32() 行(它没有任何其他可见的副作用),编译器是否只是优化了循环的其余部分?潜在的故障原因可能是 a 和 b 数组的不正确对齐,因为您使用的是对齐的加载指令。你确定它们是 16 字节对齐的吗?在当代 Intel 硬件上,16 字节对齐负载和非对齐负载之间的性能差异非常小(有关该问题的讨论,请参阅上述问题的 cmets)。
我在原始评论中提到movaps
的编码比movups
短。 这是不正确的。 我想的是 movaps
与 movapd
进行相同的内存传输,只是它们分别被标记为用于单精度和双精度数据.实际上,它们做同样的事情,但movaps
的编码更短。
【讨论】:
以上是关于使用结果浮点数时的 SSE SIMD 分段错误的主要内容,如果未能解决你的问题,请参考以下文章
SIMD,SSE,AVX - 掩码 8 浮动无符号字符? [复制]
在 X87 和 SSE FPU 中用户定义的点之后截断浮点数和双精度数