SSE浮点数组求和的向量化调用结构:哪种实现方案更优?
大浮点数组SSE向量求和的实现方案与选型探讨
针对不熟悉SSE intrinsic的开发者,我们探讨大浮点数组求和场景下,加载、加法、存储操作的最优结构。需求是通过循环持续更新结果数组,最终得到各位置的累加求和值,以下是两种朴素实现方案:
方案一:调用者循环内累加
创建临时数组存储单次128位运算结果,再与结果数组累加,代码实现如下:
#include <stdio.h> #include <xmmintrin.h> void compute (const float *a, const float *b, float *c) { __m128 va = _mm_loadu_ps(a); __m128 vb = _mm_loadu_ps(b); __m128 vc = _mm_add_ps(va, vb); _mm_storeu_ps(c, vc); } int main (void) { float a[] = { 1.1, 2.2, 3.3, 4.4, 1.1, 2.2, 3.3, 4.4 }, b[] = { 1.1, 2.2, 3.3, 4.4, 1.1, 2.2, 3.3, 4.4 }, c[4] = { 0 }; for (int i = 0; i < 8; i += 4) { float tmp[4] = { 0 }; compute (a + i, b + i, tmp); compute (c, tmp, c); } for (int i = 0; i < 4; i++) { printf ("c[%d]: %5.2f\n", i, c[i]); } }
方案二:compute函数内直接更新结果
在compute函数中完成结果数组的加载、累加与存储,代码实现如下:
#include <stdio.h> #include <xmmintrin.h> void compute (const float *a, const float *b, float *c) { __m128 va = _mm_loadu_ps(a); __m128 vb = _mm_loadu_ps(b); __m128 vt = _mm_add_ps(va, vb); __m128 vc = _mm_loadu_ps(c); __m128 vr = _mm_add_ps(vt, vc); _mm_storeu_ps(c, vr); } int main (void) { float a[] = { 1.1, 2.2, 3.3, 4.4, 1.1, 2.2, 3.3, 4.4 }, b[] = { 1.1, 2.2, 3.3, 4.4, 1.1, 2.2, 3.3, 4.4 }, c[4] = { 0 }; for (int i = 0; i < 8; i += 4) { compute (a + i, b + i, c); } for (int i = 0; i < 4; i++) { printf ("c[%d]: %5.2f\n", i, c[i]); } }
原本担忧方案一存在额外的compute函数调用开销,但实际编译后两者生成的汇编代码几乎一致,仅方案二的compute函数多一条vaddps指令。除了通过对比汇编代码判断性能差异外,是否存在通用原则可以指导这类SSE向量求和场景的方案选型?
内容的提问来源于stack exchange,提问作者David C. Rankin
相关产品推荐
相关产品推荐

