【问题标题】:Unable to optimize using SSE无法使用 SSE 进行优化
【发布时间】:2014-01-13 08:30:42
【问题描述】:

我正在测试 Zip 解密变体上的 SSE。但是,未优化的代码表现更好。

使用参数运行编译器:-msse4 -O3 会产生以下基准:-

普通测试:0.275,SSE 测试:0.655

我尝试增加循环计数器,但基准没有太大变化。编译器做了哪些优化?难道我们不能使用 SSE 来匹配它吗?

编辑:按照 Jens 的建议使用挂墙时间,增加循环迭代并修复 printf 格式。

#include <stdio.h>
#include <stdint.h>
#include <smmintrin.h>
#include <time.h>

//  Windows
#ifdef _WIN32
#include <Windows.h>
double get_wall_time()
{
    LARGE_INTEGER time,freq;
    if (!QueryPerformanceFrequency(&freq))
    {
        //  Handle error
        return 0;
    }
    if (!QueryPerformanceCounter(&time))
    {
        //  Handle error
        return 0;
    }

    return (double)time.QuadPart / freq.QuadPart;
}

double get_cpu_time()
{
    FILETIME a,b,c,d;
    if (GetProcessTimes(GetCurrentProcess(),&a,&b,&c,&d) != 0)
    {
        //  Returns total user time.
        //  Can be tweaked to include kernel times as well.
        return
            (double)(d.dwLowDateTime |
            ((unsigned long long)d.dwHighDateTime << 32)) * 0.0000001;
    }
    else
    {
        //  Handle error
        return 0;
    }
}

//  Posix/Linux
#else
#include <sys/time.h>
double get_wall_time()
{
    struct timeval time;
    if (gettimeofday(&time,NULL))
    {
        //  Handle error
        return 0;
    }

    return (double)time.tv_sec + (double)time.tv_usec * .000001;
}

double get_cpu_time()
{
    return (double)clock() / CLOCKS_PER_SEC;
}
#endif


static void test_sse()
{
    double start = get_wall_time();

    uint64_t sum = 0;

    uint32_t nk0 = 0x12345678;
    uint32_t nk1 = 0x23456789;
    uint32_t nk2 = 0x34567890;
    uint32_t nk3 = 0x45678901;

    __m128i mask = _mm_set1_epi32(0xff);

    uint64_t i;
    for(i = 0; i < 100000000; i++)
    {
        uint32_t newKeys[] = {nk0, nk1, nk2, nk3};

        __m128i *nk_sse = (__m128i*)(&newKeys);

        __m128i opa = _mm_and_si128(*nk_sse, mask);
        __m128i opr8 = _mm_srai_epi32 (*nk_sse, 8);
        __m128i opr16 = _mm_srai_epi32 (*nk_sse, 16);
        __m128i opr24 = _mm_srai_epi32 (*nk_sse, 24);

        __m128i oprsum = _mm_add_epi32(_mm_add_epi32(_mm_add_epi32(opa, _mm_and_si128(opr8, mask)), _mm_and_si128(opr16, mask)), _mm_and_si128(opr24, mask));

        uint32_t* oprsum_ptr = (uint32_t*)(&oprsum);

        uint32_t sum_sse = oprsum_ptr[0] + oprsum_ptr[1] + oprsum_ptr[2] + oprsum_ptr[3];
        sum += sum_sse;

        nk0--;
        nk1--;
        nk2--;
        nk3--;
    }

    double end = get_wall_time();

    double ms = end - start;

    printf("SSE Test - Sum: %lu, ms: %f\n", sum, ms);
}

static void test()
{
    double start = get_wall_time();

    uint64_t sum = 0;

    uint32_t nk0 = 0x12345678;
    uint32_t nk1 = 0x23456789;
    uint32_t nk2 = 0x34567890;
    uint32_t nk3 = 0x45678901;

    uint64_t i;
    for(i = 0; i < 100000000; i++)
    {
        uint8_t res0 = (uint8_t) (nk0 & 0xff);
        uint8_t res1 = (uint8_t) (nk0 >> 8);
        uint8_t res2 = (uint8_t) (nk0 >> 16);
        uint8_t res3 = (uint8_t) (nk0 >> 24);

        uint8_t res4 = (uint8_t) (nk1 & 0xff);
        uint8_t res5 = (uint8_t) (nk1 >> 8);
        uint8_t res6 = (uint8_t) (nk1 >> 16);
        uint8_t res7 = (uint8_t) (nk1 >> 24);

        uint8_t res8 = (uint8_t) (nk2 & 0xff);
        uint8_t res9 = (uint8_t) (nk2 >> 8);
        uint8_t res10 = (uint8_t) (nk2 >> 16);
        uint8_t res11 = (uint8_t) (nk2 >> 24);

        uint8_t res12 = (uint8_t) (nk3 & 0xff);
        uint8_t res13 = (uint8_t) (nk3 >> 8);
        uint8_t res14 = (uint8_t) (nk3 >> 16);
        uint8_t res15 = (uint8_t) (nk3 >> 24);

        sum += res0 + res1+ res2 + res3 + res4 + res5 + res6 + res7 + res8 + res9
        + res10 + res11 + res12 + res13 + res14  + res15;

        nk0--;
        nk1--;
        nk2--;
        nk3--;
    }

    double end = get_wall_time();

    double ms = end - start;

    printf("Normal Test - Sum: %lu, ms: %f\n", sum, ms);
}

int main (int argc, char **argv)
{
    test();
    test_sse();
    return 0;
}

【问题讨论】:

  • 为什么用-Os编译?
  • 我的错,我打算用 -O3 来做。以下是 -O3 的结果:- 正常 - 2,SSE - 6
  • @Partho BTW,你读过非看test的反汇编吗?当给定 -msse4 时,编译器 本身 也可能使用 SSE 指令(至少 GCC 承诺类似的东西)。

标签: c sse


【解决方案1】:

您使用了错误的工具来衡量绩效。 clock,至少在符合标准的平台上,为您提供 CPU 时间而不是挂钟时间。

您做错的另一件事是使用%d 打印uint64_t。当int 少于 64 位时,这具有未定义的行为。您当时打印的int 可能会收到垃圾邮件。

编辑: 现在您已经修复了您的代码,我已将其编译为汇编程序(gcc 选项-S)。事实上 gcc 在矢量化和展开 test 函数方面做得很好。使用我拥有的三个编译器,我得到了完全不同的结果,全部使用 -O3 -march=native 编译

icc:

Normal Test - Sum: 169248636030, ms: 0.618356
SSE Test - Sum: 169248636030, ms: 1.059261

gcc 4.6:

Normal Test - Sum: 169248636030, ms: 0.462793
SSE Test - Sum: 169248636030, ms: 0.348453

叮当声:

Normal Test - Sum: 169248636030, ms: 0.625905
SSE Test - Sum: 169248636030, ms: 0.423343

所以 icc 和 clang 在未优化的代码上做的差不多,但 gcc 做得更好。使用 gcc 和 clang,您的 sse 代码会更好,而 icc 则更宽松。总的来说,与公共领域的“免费”编译器相比,商业编译器 icc 的性能确实令人失望。

编辑 2:

现在有了更新版本的 gcc,我什至得到了这个

gcc 4.7:

Normal Test - Sum: 169248636030, ms: 0.158695
SSE Test - Sum: 169248636030, ms: 0.406921

因此,您看到“正常测试”进一步提高了,而 SSE测试比以前差了一点。

【讨论】:

  • 感谢您的信息。我使用了here 的挂钟时间解决方案。还修复了 printf 格式。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-12-09
  • 2018-06-10
  • 2018-01-12
  • 2011-12-16
  • 1970-01-01
  • 1970-01-01
  • 2012-07-09
相关资源
最近更新 更多