我正在对一些 SSE 进行基准测试代码(4 个 float 乘以 4 个 float )与传统 C 代码做同样的事情。我认为我的基准测试代码在某种程度上一定是不正确的,因为它似乎说非 SSE 代码比 SSE 快 2-3 倍。
有人可以告诉我下面的基准测试代码有什么问题吗?也许建议另一种方法来准确显示 SSE 和非 SSE 代码的速度。
#include <time.h>
#include <string.h>
#include <stdio.h>
#define ITERATIONS 100000
#define MULT_FLOAT4(X, Y) ({ \
asm volatile ( \
"movaps (%0), %%xmm0\n\t" \
"mulps (%1), %%xmm0\n\t" \
"movaps %%xmm0, (%1)" \
:: "r" (X), "r" (Y)); })
int main(void)
{
int i, j;
float a[4] __attribute__((aligned(16))) = { 10, 20, 30, 40 };
time_t timer, sse_time, std_time;
timer = time(NULL);
for(j = 0; j < 5000; ++j)
for(i = 0; i < ITERATIONS; ++i) {
float b[4] __attribute__((aligned(16))) = { 0.1, 0.1, 0.1, 0.1 };
MULT_FLOAT4(a, b);
}
sse_time = time(NULL) - timer;
timer = time(NULL);
for(j = 0; j < 5000; ++j)
for(i = 0; i < ITERATIONS; ++i) {
float b[4] __attribute__((aligned(16))) = { 0.1, 0.1, 0.1, 0.1 };
b[0] *= a[0];
b[1] *= a[1];
b[2] *= a[2];
b[3] *= a[3];
}
std_time = time(NULL) - timer;
printf("sse_time %d\nstd_time %d\n", sse_time, std_time);
return 0;
}
最佳答案
当您启用优化时,非 SSE 代码将被完全消除,而 SSE 代码仍保留在那里,因此这种情况很简单。更有趣的部分是当优化关闭时:在这种情况下,SSE 代码仍然较慢,而循环代码是相同的。
最内层循环体的非 SSE 代码:
movl $0x3dcccccd, %eax
movl %eax, -80(%rbp)
movl $0x3dcccccd, %eax
movl %eax, -76(%rbp)
movl $0x3dcccccd, %eax
movl %eax, -72(%rbp)
movl $0x3dcccccd, %eax
movl %eax, -68(%rbp)
movss -80(%rbp), %xmm1
movss -48(%rbp), %xmm0
mulss %xmm1, %xmm0
movss %xmm0, -80(%rbp)
movss -76(%rbp), %xmm1
movss -44(%rbp), %xmm0
mulss %xmm1, %xmm0
movss %xmm0, -76(%rbp)
movss -72(%rbp), %xmm1
movss -40(%rbp), %xmm0
mulss %xmm1, %xmm0
movss %xmm0, -72(%rbp)
movss -68(%rbp), %xmm1
movss -36(%rbp), %xmm0
mulss %xmm1, %xmm0
movss %xmm0, -68(%rbp)
最内层循环体的SSE代码:
movl $0x3dcccccd, %eax
movl %eax, -64(%rbp)
movl $0x3dcccccd, %eax
movl %eax, -60(%rbp)
movl $0x3dcccccd, %eax
movl %eax, -56(%rbp)
movl $0x3dcccccd, %eax
movl %eax, -52(%rbp)
leaq -48(%rbp), %rax
leaq -64(%rbp), %rdx
movaps (%rax), %xmm0
mulps (%rdx), %xmm0
movaps %xmm0, (%rdx)
我对此不确定,但这是我的猜测:
正如您所看到的,编译器仅通过 4 个 32 位存储来存储 4 个浮点值。然后由 16 字节负载读回。这会导致存储转发停顿,一旦发生,代价高昂。您可以在英特尔手册中查找此内容。它不会发生在标量版本中,这会导致性能差异。
为了使其更快,您需要确保不会发生这种停顿。如果您使用 4 个 float 的常量数组,请将其设为常量并将结果存储在另一个对齐数组中。这样编译器有望在加载之前不会进行那些不必要的 4 字节 movs。或者,如果您需要填充结果数组,请使用 16 字节存储命令来完成。如果您无法避免这些 4 字节 mov,则需要在存储之后但加载之前执行其他操作(例如计算其他操作)。
关于gcc - 对标上交所指令,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1733688/