为什么不std :: count和std :: find优化使用memchr?

我正在阅读sehe对这个问题的回答,很惊讶地看到使用std::memchr手写循环比使用std::count (见注释) 快了3倍 。 使用std::count的代码可以在编辑2中看到,但它基本归结为:

const auto num_lines = std::count(f, l, 'n');

VS

uintmax_t num_lines = 0;
while (f && f != l)
    if ((f = static_cast<const char*>(memchr(f, 'n', l - f))))
        num_lines++, f++;

我希望std::count版本至少和std::memchr版本一样快 - 这也是为什么std::copy应该至少和std::memcpy一样快的原因。

我检查了我的标准库(libc ++) std::count ,并且没有试图针对char输入类型进行优化(对于std::find同上)。

为什么是这样? 如果提供了char*迭代器和char值,实现是否可以不派遣到std::memchr


如果匹配之间的平均距离不小,那么对memchr使用实际函数调用只会是一场胜利。

特别是对于count ,如果您计算t字符时平均每2或4(例如,使用ACGT字母表的DNA碱基对),则调用memchr可能会慢很多。

我会怀疑使用memchr循环作为char数组的std::count的默认实现。 更有可能的其他方式来调整来源,使其编译更好。

对于find它会更有意义,即使它可能会显着增加开销,而对于一个简单的一次一个字节的循环,如果在第一对字节中有一个命中的话。


您也可以将其视为编译器错失优化。 如果编译器为std::countstd::find的循环创建了更好的代码,那么通过调用手写的asm库函数就可以获得更少的代码。

gcc和clang在进入循环之前并不知道行程计数时不会自动矢量化循环。 (即它们不会执行搜索循环,这对于像字节一样小的元素大小而言是主要的遗漏优化)。 ICC没有这种限制,可以矢量化搜索循环。 尽管如此,我还没有看过它是如何处理libc ++的std :: count或find的。

std::count必须检查每个元素,所以它应该自动向量化。 但如果海湾合作委员会或铿锵甚至不与-O3 ,那么这是不幸的。 它应该在x86上使用pcmpeqb (打包比较字节)很好地进行矢量化,然后paddb为0 / -1比较结果。 (至少每255次迭代, psadbw对零来水平地求和字节元素)。

库函数调用开销至少是一个从内存中调用函数指针的间接调用(可以缓存未命中)。 在具有动态链接的Linux上,通常还有一个额外的jmp通过PLT(除非使用-fno-plt编译)。 memchrstrchr更容易优化,启动开销strchr ,因为您可以快速检查16B矢量负载是否可以越过末端(对比指向strchrstrlen的指针以避免跨越页面或缓存行边界)

如果调用memchr是用asm实现某些东西的最佳方式,那么理论上这就是编译器应该发出的东西。 gcc / clang已经根据目标选项( -march= )优化了大型复制循环以调用libc memcpy 。 例如,当副本足够大,libc版本可能决定在x86上使用NT存储时。

链接地址: http://www.djcxy.com/p/39137.html

上一篇: Why aren't std::count and std::find optimised to use memchr?

下一篇: Using Lists Instead of Decorator Pattern?