【问题标题】:SIMD Program slow runtimeSIMD 程序运行缓慢
【发布时间】:2018-05-18 09:53:20
【问题描述】:

我开始使用 SIMD 编程,但我现在不知道该做什么。我正在尝试减少运行时间,但它以另一种方式进行。

这是我的基本代码: https://codepaste.net/a8ut89

void blurr2(double * u, double * r) {

    int i;
    double dos[2] = { 2.0, 2.0 };

    for (i = 0; i < SIZE - 1; i++) {
        r[i] = u[i] + u[i + 1];
    }
}

模糊2:0.43s

int contarNegativos(double * u) {

    int i;
    int contador = 0;

    for (i = 0; i < SIZE; i++) {
        if (u[i] < 0) {
            contador++;
        }
    }
    return contador;
}

负数:1.38s

void ord(double * v, double * u, double * r) {

    int i;

    for (i = 0; i < SIZE; i += 2) {
        r[i] = *(__int64*)&(v[i]) | *(__int64*)&(u[i]);
    }
}

ord:0.33


这是我的 SIMD 代码:

https://codepaste.net/fbg1g5

void blurr2(double * u, double * r) {

    __m128d rp2;
    __m128d rdos;
    __m128d rr;
    int i;
    int sizeAux = SIZE % 2 == 1 ? SIZE : SIZE - 1;
    double dos[2] = { 2.0, 2.0 };

    rdos = *(__m128d*)dos;

    for (i = 0; i < sizeAux; i += 2) {
        rp2 = *(__m128d*)&u[i + 1];
        rr = _mm_add_pd(*(__m128d*)&u[i], rp2);
        *((__m128d*)&r[i]) = _mm_div_pd(rr, rdos);
    }
}

模糊2:0.42s


int contarNegativos(double * u) {

    __m128d rcero;
    __m128d rr;
    int i;
    double cero[2] = { 0.0, 0.0 };
    int contador = 0;

    rcero = *(__m128d*)cero;

    for (i = 0; i < SIZE; i += 2) {
        rr = _mm_cmplt_pd(*(__m128d*)&u[i], rcero);
        if (((__int64 *)&rr)[0]) {
            contador++;
        };
        if (((__int64 *)&rr)[1]) {
            contador++;
        };
    }
    return contador;
}

负数:1.42s


void ord(double * v, double * u, double * r) {

    __m128d rr;
    int i;

    for (i = 0; i < SIZE; i += 2) {
        *((__m128d*)&r[i]) = _mm_or_pd(*(__m128d*)&v[i], *(__m128d*)&u[i]);
    }
}

ord:0.35s

**不同的解决方案。


你能解释一下我做错了什么吗?我有点迷茫……

【问题讨论】:

  • 请发布显示问题的Minimal, Complete, and Verifiable example。将输入、预期输出和实际输出显示为问题中的文本。我不会点击你的链接。
  • 您是否检查过编译器是否已经在“基本代码”案例中为您自动矢量化了代码?
  • @geza 他们是不同的解决方案
  • blurr2:乘以 0.5 而不是除以 2。它会快得多。 (我在最后一两天用完全相同的代码对一个问题发表了同样的评论,你也是吗?)
  • @PeterCordes 哎呀,旧代码,我改了,它更快了! (是的,但他们删除了我的问题不知道为什么......)

标签: c++ c performance sse simd


【解决方案1】:

你知道ord 函数与 SIMD 不是 1:1 到 ord 函数而不使用 SIMD 指令吗?

ord函数中不使用SIMD,OR运算的结果是针对偶数索引计算的

r[0] = v[0] | u[0], 
r[2] = v[2] | u[2], 
r[4] = v[4] | u[4]

奇数索引怎么办?也许,如果对所有索引都计算 OR 操作,它会比现在花费更多的时间。

【讨论】:

    【解决方案2】:

    使用_mm_loadu_pd 而不是指针转换和取消引用__m128d。保证您的代码在 gcc/clang 上出现段错误,其中假定 __m128d 是对齐的。


    blurr2:乘以 0.5 而不是除以 2。它会快得多。 (我在前一两天用完全相同的代码对一个问题发表了同样的评论,你也是吗?)


    negativeCount:_mm_castpd_si128 比较结果为整数,并与_mm_sub_epi64 累加。 (位模式为全零或全一,即 2 的补码 0 / -1)。

    #include <immintrin.h>
    #include <stdint.h>
    
    static const size_t SIZE = 1024;
    
    uint64_t countNegative(double * u) {
        __m128i counts = _mm_setzero_si128();
        for (size_t i = 0; i < SIZE; i += 2) {
            __m128d cmp = _mm_cmplt_pd(_mm_loadu_pd(&u[i]), _mm_setzero_pd());
            counts = _mm_sub_epi64(counts, _mm_castpd_si128(cmp));
        }
    
        //return counts[0] + counts[1];  // GNU C only, and less efficient
        // horizontal sum
        __m128i hi64  = _mm_shuffle_epi32(counts, _MM_SHUFFLE(1, 0, 3, 2));
        counts = _mm_add_epi64(counts, hi64);
    
        uint64_t scalarcount = _mm_cvtsi128_si64(counts);
        return scalarcount;
    }
    

    要了解有关有效向量水平和的更多信息,请参阅Fastest way to do horizontal float vector sum on x86。但第一条规则是在循环之外进行

    (source + asm on the Godbolt compiler explorer)

    从 MSVC(我猜你正在使用,或者你会从 *(__m128d*)foo 得到段错误),内部循环是:

    $LL4@countNegat:
        movups   xmm0, XMMWORD PTR [rcx]
        lea      rcx, QWORD PTR [rcx+16]
        cmpltpd xmm0, xmm2
        psubq    xmm1, xmm0
        sub      rax, 1
        jne      SHORT $LL4@countNegat
    

    展开可能会更快(可能还有两个矢量累加器),但这相当不错,在 Sandybridge/Haswell 上可能会接近每 16 字节 1.25 个时钟。 (5 个融合域微指令的瓶颈)。

    你的版本实际上是在 inside 内部循环中解压成整数!如果你使用 MSVC -Ox,它实际上是分支而不是使用无分支比较 + 条件添加。我很惊讶它没有比标量版本慢。

    另外,(int64_t *)&amp;rr 违反了严格的别名。 char* 可以给任何东西起别名,但是将其他指针投射到 SIMD 向量上并期望它能够工作是不安全的。如果是这样,你很幸运。编译器通常会为那个或内在函数生成类似的代码,并且对于正确的内在函数通常不会更糟。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-12-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多