SSE内在优化

时间:2016-05-14 06:37:35

标签: c sse simd

我是SSE内在函数的新手,并尝试通过它来优化我的代码。这是关于计算与给定值相等的数组元素的程序。

我将代码更改为SSE版本,但速度几乎没有变化。我想知道我是否以错误的方式使用SSE ......

此代码用于我们不允许启用编译器优化选项的分配。

没有SSE版本:

int get_freq(const float* matrix, float value) {

    int freq = 0;

    for (ssize_t i = start; i < end; i++) {
        if (fabsf(matrix[i] - value) <= FLT_EPSILON) {
            freq++;
        }
    }

    return freq;
}

SSE版本:

#include <immintrin.h>
#include <math.h>
#include <float.h>

#define GETLOAD(n) __m128 load##n = _mm_load_ps(&matrix[i + 4 * n])
#define GETEQU(n) __m128 check##n = _mm_and_ps(_mm_cmpeq_ps(load##n, value), and_value)
#define GETCOUNT(n) count = _mm_add_ps(count, check##n)

    int get_freq(const float* matrix, float givenValue, ssize_t g_elements) {

        int freq = 0;
        int i;

        __m128 value = _mm_set1_ps(givenValue);
        __m128 count = _mm_setzero_ps();
        __m128 and_value = _mm_set1_ps(0x00000001);


        for (i = 0; i + 15 < g_elements; i += 16) {
            GETLOAD(0); GETLOAD(1); GETLOAD(2); GETLOAD(3);
            GETEQU(0);  GETEQU(1);  GETEQU(2);  GETEQU(3);
            GETCOUNT(0);GETCOUNT(1);GETCOUNT(2);GETCOUNT(3);
        }

        __m128 shuffle_a = _mm_shuffle_ps(count, count, _MM_SHUFFLE(1, 0, 3, 2));
        count = _mm_add_ps(count, shuffle_a);
        __m128 shuffle_b = _mm_shuffle_ps(count, count, _MM_SHUFFLE(2, 3, 0, 1));
        count = _mm_add_ps(count, shuffle_b);
        freq = _mm_cvtss_si32(count);


        for (; i < g_elements; i++) {
            if (fabsf(matrix[i] - givenValue) <= FLT_EPSILON) {
                freq++;
            }
        }

        return freq;
    }

2 个答案:

答案 0 :(得分:4)

如果需要使用-O0进行编译,请在单个语句中尽可能多地进行编译。在普通代码中,int a=foo(); bar(a);将编译为与bar(foo())相同的asm,但在-O0代码中,第二个版本可能会更快,因为它不会将结果存储到内存然后重新加载它以用于下一个语句。

-O0旨在通过调试提供最可预测的结果,这就是为什么在每个语句之后所有内容都存储到内存中的原因。这显然是表现糟糕的。

我写了a big answer a while ago来处理来自其他人的不同问题,其中包含像你这样的愚蠢任务,要求他们针对-O0进行优化。其中一些可能有所帮助。

不要试图努力完成这项任务。可能大多数&#34;技巧&#34;您认为使用-O0让代码运行得更快只会对-O0产生影响,但对启用优化没有任何影响。

在现实生活中,代码通常至少使用clang或gcc -O2进行编译,有时使用-O3 -march=haswell或其他任何内容进行自动向量化。 (一旦调试完毕,您就可以进行优化了。)

Re:你的更新:

现在它编译了,可以看到来自SSE版本的可怕的asm。我把它on godbolt along with a version of the scalar code that actually compiles, too

标量版的出现不那么糟糕。它的源代码在一个表达式中完成所有内容,因此临时代码保留在寄存器中。然而,循环计数器仍然在内存中,例如,在Haswell上每6个循环最多只能进行一次迭代。 (有关优化资源,请参阅标记wiki。)

BTW,矢量化fabsf()很简单,请参阅Fastest way to compute absolute value using SSE。那个和SSE比较少于应该做的技巧,给你与标量代码相同的语义。 (但让-O0更难以吸吮更难。)

您可能只需手动展开标量版本一次或两次,因为-O0太糟糕了。

答案 1 :(得分:1)

有些编译器非常适合做矢量优化。您是否检查了两个版本的优化版本的生成程序集?不是天真的&#34;版本实际上使用SIMD或其他优化技术?