【问题标题】:Benchmarking SSE instructions基准 SSE 指令
【发布时间】:2009-11-14 08:31:20
【问题描述】:

我正在对一些 SSE 代码(将 4 个浮点数乘以 4 个浮点数)与做同样事情的传统 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;
}

【问题讨论】:

    标签: gcc assembly benchmarking sse


    【解决方案1】:

    当您启用优化时,非 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 个浮点值存储在 4 个 32 位存储中。然后通过 16 字节加载读回。这会导致存储转发停顿,这在发生时代价高昂。您可以在英特尔手册中查找此内容。它不会出现在标量版本中,这会导致性能差异。

    为了加快速度,您需要确保不会发生这种停顿。如果您使用 4 个浮点数的常量数组,请将其设为 const 并将结果存储在另一个对齐的数组中。这样,编译器希望不会在加载之前进行那些不必要的 4 字节移动。或者,如果您需要填充结果数组,请使用 16 字节存储命令来完成。如果您无法避免那些 4 字节的 mov,则需要在存储之后但在加载之前执行其他操作(例如计算其他内容)。

    【讨论】:

    • 感谢您的回答。 :) 但是我真的需要一个非常快的 4 浮点乘法器,使用 SSE 进行图像处理。如果我使用的方法存在某种缺陷,您能否提出另一种利用 SSE 的强大功能但不会导致速度下降的方法。我读到 SSE 实际上是为我想到的那种图像处理而设计的,所以肯定有 必须 有一种方法来做我想做的事吗? (我需要快速的 4float 乘法器来进行 alpha 混合等操作)
    • 谢谢,我会查找“商店转运摊位”并尝试了解这里发生的事情
    猜你喜欢
    • 2010-10-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-08
    相关资源
    最近更新 更多