memchr()如何在引擎盖下工作?

时间:2009-02-08 03:52:49

标签: c performance d standard-library

背景:我正在尝试创建一个纯D语言实现,其功能大致相当于C's memchr但使用数组和索引而不是指针。原因是std.string将用于编译时功能评估。对于那些不熟悉w / D的人,如果满足某些限制,可以在编译时评估函数。一个限制是它们不能使用指针。另一个是他们不能调用C函数或使用内联汇编语言。让字符串库在编译时工作对于某些编译时代码生成非常有用。

问题:memchr如何以最快的速度运行?在Win32上,我使用简单循环在纯D中创建的任何东西,即使有明显的优化技术,例如禁用边界检查,循环展开等,也至少要慢2倍。有哪些非显而易见的技巧可用于像在字符串中查找字符一样简单吗?

5 个答案:

答案 0 :(得分:12)

我建议看看GNU libc的来源。对于大多数函数,它将包含函数的通用优化C版本,以及尽可能多的支持体系结构的优化汇编语言版本,利用机器特定的技巧。

x86-64 SSE2 versionpcmpeqb的结果一次性整合到整个缓存行数据(四个16B向量),以分摊早期退出pmovmskb / {的开销{1}} / test

gcc和clang目前无法使用jcc早期退出条件自动向量化循环,因此它们可以从明显的C实现中逐个进行字节化。

答案 1 :(得分:7)

This implementation of memchr from newlib是某人优化记忆的一个例子: 它一次读取和测试4个字节(除了memchr,newlib库中的其他函数是here)。

顺便提一下,MSVC运行时库的大多数源代码都是可用的,作为MSVC安装的可选部分(因此,你可以看一下)。

答案 2 :(得分:5)

这是来自memchr.c的FreeBSD(BSD许可)memchr()。 FreeBSD的在线源代码浏览器是经过时间考验的BSD许可代码示例的一个很好的参考。

void *
memchr(s, c, n)
    const void *s;
    unsigned char c;
    size_t n;
{
    if (n != 0) {
        const unsigned char *p = s;

        do {
            if (*p++ == c)
                return ((void *)(p - 1));
        } while (--n != 0);
    }
    return (NULL);
}

答案 3 :(得分:2)

像memset和memcpy这样的memchr通常会减少到相当少量的机器代码。没有inlining similar assembly code,你不太可能重现那种速度。实施中需要考虑的一个主要问题是data alignment

一个generic technique you may be able to use是在搜索字符串的末尾插入sentinel,这样可以保证您找到它。它允许您将字符串结尾的测试从循环内部移动到循环之后。

答案 4 :(得分:0)

GNU libc肯定使用memchr()的 assembly 版本(在任何常见的Linux发行版上)。这就是为什么它是如此之快的原因。
例如,如果我们计算11Gb文件中的行数(例如“ wc -l <​​/ em>”那样),则大约需要 2.5 秒,其中包含GNU libc的 assembly 版本的memchr()。但是,如果我们用FreeBSD中的memchr() C实现替换memchr()程序集调用-速度将降低到 30 秒。

这等于只用一个while循环替换memchr(),该循环将一个字符与另一个字符进行比较。