【发布时间】:2019-06-16 23:04:56
【问题描述】:
为什么我的 SIMD vector4 长度函数比简单的向量长度方法慢 3 倍?
SIMD vector4 长度函数:
__extern_always_inline float vec4_len(const float *v) {
__m128 vec1 = _mm_load_ps(v);
__m128 xmm1 = _mm_mul_ps(vec1, vec1);
__m128 xmm2 = _mm_hadd_ps(xmm1, xmm1);
__m128 xmm3 = _mm_hadd_ps(xmm2, xmm2);
return sqrtf(_mm_cvtss_f32(xmm3));
}
天真的实现:
sqrtf(V[0] * V[0] + V[1] * V[1] + V[2] * V[2] + V[3] * V[3])
SIMD 版本需要 16110 毫秒来迭代 1000000000 次。 naive 版本快了约 3 倍,只需要 4746 毫秒。
#include <math.h>
#include <time.h>
#include <stdint.h>
#include <stdio.h>
#include <x86intrin.h>
static float vec4_len(const float *v) {
__m128 vec1 = _mm_load_ps(v);
__m128 xmm1 = _mm_mul_ps(vec1, vec1);
__m128 xmm2 = _mm_hadd_ps(xmm1, xmm1);
__m128 xmm3 = _mm_hadd_ps(xmm2, xmm2);
return sqrtf(_mm_cvtss_f32(xmm3));
}
int main() {
float A[4] __attribute__((aligned(16))) = {3, 4, 0, 0};
struct timespec t0 = {};
clock_gettime(CLOCK_MONOTONIC, &t0);
double sum_len = 0;
for (uint64_t k = 0; k < 1000000000; ++k) {
A[3] = k;
sum_len += vec4_len(A);
// sum_len += sqrtf(A[0] * A[0] + A[1] * A[1] + A[2] * A[2] + A[3] * A[3]);
}
struct timespec t1 = {};
clock_gettime(CLOCK_MONOTONIC, &t1);
fprintf(stdout, "%f\n", sum_len);
fprintf(stdout, "%ldms\n", (((t1.tv_sec - t0.tv_sec) * 1000000000) + (t1.tv_nsec - t0.tv_nsec)) / 1000000);
return 0;
}
我在 Intel(R) Core(TM) i7-8550U CPU 上运行以下命令。首先使用 vec4_len 版本,然后使用普通 C。
我用 GCC (Ubuntu 7.4.0-1ubuntu1~18.04.1) 7.4.0 编译:
gcc -Wall -Wextra -O3 -msse -msse3 sse.c -lm && ./a.out
SSE 版本输出:
499999999500000128.000000
13458ms
纯 C 版本输出:
499999999500000128.000000
4441ms
【问题讨论】:
-
haddps很糟糕,3 倍听起来还是很多,反汇编有什么有趣的地方吗? -
您如何对此进行基准测试?你确定标量版本没有提升一些工作吗?什么硬件上的编译器/版本,以及内联到的周围代码是什么?此外,由于您在
__m128i中具有您的值,因此您可以通过使用_mm_sqrt_ss来避免sqrtf()的愚蠢的 set-errno-on-NaN 行为,因此您不必使用-fno-math-errno进行编译跨度> -
需要编译器版本。 minimal reproducible example 必填。 Godbolt 链接很好。
-
您的实际用例是什么?偶尔计算单个向量的范数,还是计算一个向量序列的范数?您能否重新组织您的数据 (SoA vs AoS)?
-
看起来编译在优化方面比godbolt更好。您的函数对于任意数据看起来更快(程序集更短),但是您测量执行速度的方法有点无效,编译器可以更好地优化循环。您需要使用两个函数并告诉编译器副作用。前使用
__attribute__((__noinline__)),如something like this。
标签: c compiler-optimization sse simd microbenchmark