所以我做了一个strlen函数,我想我应该用SIMD指令来实现它。它运行得很好,但是当将它与std::strlen()进行比较时,它就落后了很多。任何关于如何使它更快的想法将是非常感谢的。
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#发布于 2021-02-19 01:46:45
pcmpistri这似乎是搜索字符串的明显选择。然而,虽然pcmpistri非常通用/强大,但它也不是很快。在典型的英特尔处理器上,它由3个ops组成,它们全部进入执行端口p0 (因此限制这个循环最多每3个周期运行一次迭代),在AMD (1/2)上,在2个ops和每2个循环执行一次的情况下,情况稍微好一些。
有一种基于pcmpeqb和pmovmskb的更原始的方法(仅使用D5)。这给您留下了一个掩码,而不是索引,但是对于大多数不重要的循环(重要的是掩码是否为零),在最后的迭代中,您可以使用tzcnt (或类似的)在向量中找到零字节的实际索引。
该技术还可以扩展到AVX2,而pcmpistri没有。此外,您可以使用一些展开:pminub一些连续的16字节块一起来更快地完成字符串,而代价是更复杂的最后迭代和更复杂的预循环设置(请参见下一点)。
虽然包含至少一个字节的字符串的对齐负载是安全的,即使加载将某些数据拉入字符串界限之外(对齐负载不能跨越页面边界),但对于未对齐的负载来说,这个技巧是不安全的。在页面边界附近结束的字符串可能导致此函数获取到下一个页面,并可能触发访问冲突。
有不同的方法来解决这个问题。最明显的是使用通常的逐字节循环,直到到达足够对齐的地址为止。一个更高级的技巧是将地址舍入到16的倍数(32表示AVX2)并执行对齐负载。其中有些字节不是来自字符串,可能包括一个零。因此,必须显式忽略这些字节,例如,将pmovmskb返回的掩码由data & 15移到右侧。如果您决定添加展开,那么主循环的地址应该更对齐,以确保主循环主体中的所有负载都是安全的。
https://codereview.stackexchange.com/questions/256212
复制相似问题