加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

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

发布时间:2020-12-16 03:22:06 所属栏目:百科 来源:网络整理
导读:我正在尝试优化以下函数(简化了一下,但它是我的程序花了很多时间的循环): int f(int len,unsigned char *p) { int i = 0; while (i len p[i] = 32 p[i] = 127) { i++; } return i;} 我认为可以使用向量指令进行优化,但是从一些研究来看,SSE似乎不适合在字节
我正在尝试优化以下函数(简化了一下,但它是我的程序花了很多时间的循环):
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位版本之间可能差别不大.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读