首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Strlen函数优化

Strlen函数优化
EN

Code Review用户
提问于 2021-02-19 01:03:06
回答 1查看 364关注 0票数 3

所以我做了一个strlen函数,我想我应该用SIMD指令来实现它。它运行得很好,但是当将它与std::strlen()进行比较时,它就落后了很多。任何关于如何使它更快的想法将是非常感谢的。

代码语言:javascript
复制
inline size_t strlen(const char* data) {
        const __m128i terminationCharacters = _mm_setzero_si128();
        const __m128i* pointer = (const __m128i*)data;

        size_t length = 0;

        const int compareMode = _SIDD_UBYTE_OPS | _SIDD_LEAST_SIGNIFICANT | _SIDD_CMP_EQUAL_EACH;

        for (;; length += 16, ++pointer) {
            const __m128i comparingData = _mm_loadu_si128(pointer);

            size_t index = _mm_cmpistri(terminationCharacters, comparingData, compareMode);

            if (index < 16)
                return length + index;
        }
    }
```#qcStackCode#
代码语言:javascript
复制
EN

回答 1

Code Review用户

回答已采纳

发布于 2021-02-19 01:46:45

pcmpistri

这似乎是搜索字符串的明显选择。然而,虽然pcmpistri非常通用/强大,但它也不是很快。在典型的英特尔处理器上,它由3个ops组成,它们全部进入执行端口p0 (因此限制这个循环最多每3个周期运行一次迭代),在AMD (1/2)上,在2个ops和每2个循环执行一次的情况下,情况稍微好一些。

有一种基于pcmpeqbpmovmskb的更原始的方法(仅使用D5)。这给您留下了一个掩码,而不是索引,但是对于大多数不重要的循环(重要的是掩码是否为零),在最后的迭代中,您可以使用tzcnt (或类似的)在向量中找到零字节的实际索引。

该技术还可以扩展到AVX2,而pcmpistri没有。此外,您可以使用一些展开:pminub一些连续的16字节块一起来更快地完成字符串,而代价是更复杂的最后迭代和更复杂的预循环设置(请参见下一点)。

A bug

虽然包含至少一个字节的字符串的对齐负载是安全的,即使加载将某些数据拉入字符串界限之外(对齐负载不能跨越页面边界),但对于未对齐的负载来说,这个技巧是不安全的。在页面边界附近结束的字符串可能导致此函数获取到下一个页面,并可能触发访问冲突。

有不同的方法来解决这个问题。最明显的是使用通常的逐字节循环,直到到达足够对齐的地址为止。一个更高级的技巧是将地址舍入到16的倍数(32表示AVX2)并执行对齐负载。其中有些字节不是来自字符串,可能包括一个零。因此,必须显式忽略这些字节,例如,将pmovmskb返回的掩码由data & 15移到右侧。如果您决定添加展开,那么主循环的地址应该更对齐,以确保主循环主体中的所有负载都是安全的。

票数 6
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/256212

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档