c – 这个功能是英特尔SIMD的理想选择吗?

前端之家收集整理的这篇文章主要介绍了c – 这个功能是英特尔SIMD的理想选择吗?前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我正在尝试优化以下函数(简化了一下,但它是我的程序花了很多时间的循环):
int f(int len,unsigned char *p) {
  int i = 0;
  while (i < len && p[i] >= 32 && p[i] <= 127) {
      i++;
  }
  return i;
}

我认为可以使用向量指令进行优化,但是从一些研究来看,SSE似乎不适合在字节级工作.该程序仅针对OSX上的64位Intel cpu.有没有一个巧妙的比特伎俩,我没有看到它会让我一次只能处理64位? llvm与-O3没有做任何聪明的优化.

更新:

SIMD代码在我的基准测试中通常是最快的(取决于输入的大小),但由于某种原因,使用SIMD的应用程序总体上比使用简单的代码或比特笨拙的技巧更慢.对于上下文,应用程序在终端仿真器的输入流中查找ASCII字符串的子序列的长度. ASCII字符串得到特殊的“快速路径”处理.我只能将一个答案标记为正确,但两者都很棒.我确实做了一个改进的小改进,通过这样做删除if语句:

while (i < len - 8) {
            uint64_t bytes = *(uint64_t *)(p + i);
            uint64_t middleBits = bytes & 0x6060606060606060;
            uint64_t highBits = bytes & 0x8080808080808080;
            middleBits |= (middleBits >> 1);
            middleBits &= ~(highBits >> 2);
            if ((middleBits & 0x2020202020202020) != 0x2020202020202020) {
                break;
            }
            i += 8;
        }

解决方法

您可以使用_mm_cmplt_epi8和_mm_cmpgt_epi8(msvc intrinsics)对比较进行矢量化.

然后,您可以对ANDing比较结果的结果使用movemask.如果movemask的结果是0xFFFF,那么所有比较都会通过.否则,您需要运行尾循环以找出未通过测试的正确位置.你可以从面具中找出这个,但是根据’len’的值,它可能不值得努力.

如果’len’不是16的倍数,则还需要原始的非尾部非循环循环.它可能会也可能不会更快 – 您需要对其进行分析以确定.

报废 – 比较对签名值进行操作,但不起作用..

下面的工作版本.

union UmmU8 {
    __m128i mm_;
    struct {
        unsigned char u8_;
    };
};

int f(int len,unsigned char *p) {
    int i = 0;
    __m128i A;
    __m128i B;
    __m128i C;
    UmmU8* pu = (UmmU8*)p;    
    int const len16 = len / 16;
    while (i < len16) {
        A = pu[i].mm_;
        B = _mm_slli_epi32(A,1);
        C = _mm_slli_epi32(A,2);
        B = _mm_or_si128(B,C);
        A = _mm_andnot_si128(A,B);

        int mask = _mm_movemask_epi8(A);
        if (mask == 0xFFFF) {
            ++i;
        }
        else {
            if (mask == 0) {
                return i * 16;
            }
            break;
        }
    }
    i *= 16;
    while (i < len && p[i] >= 32 && p[i] <= 127) {
        i++;
    }
    return i;
}

由于我在这台PC上没有64 OS,我不能进行适当的性能测试.
但是,分析运行给出了:

>天真循环:30.44
> 64位整数:15.22(在32位操作系统上)
> SSE impl:5.21

所以SSE版本比朴素循环版本快得多.我希望64位版本在64位系统上表现更好 – SSE和64位版本之间可能差别不大.

猜你在找的C&C++相关文章