【问题标题】:How does memchr() work under the hood?memchr() 如何在后台工作?
【发布时间】:2009-02-08 03:52:49
【问题描述】:

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

问题: memchr 是如何在后台工作以实现如此快速的性能的?在 Win32 上,我能够使用简单循环在纯 D 中创建的任何内容都至少慢 2 倍,即使使用明显的优化技术,例如禁用边界检查、循环展开等。有哪些不明显的技巧可用于像在字符串中查找字符一样简单?

【问题讨论】:

    标签: c performance d standard-library


    【解决方案1】:

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

    x86-64 SSE2 versionpcmpeqb 的结果一次合并到整个缓存行数据(四个 16B 向量),以分摊提前退出 pmovmskb/test/jcc 的开销.

    gcc 和 clang 目前无法自动矢量化具有 if() break 提前退出条件的循环,因此它们从明显的 C 实现中生成了简单的 byte-at-a-time asm。

    【讨论】:

    • 谢谢,除了这是 LGPL 代码和 D 的标准库应该是许可许可的。我不希望这成为一个问题。
    • 好吧,我建议你看看它是为了获得技术灵感,而不是复制源代码。
    • 大约有 150 行代码,其中大约一半或更多是 cmets,因此它非常详细地解释了优化。
    • stackoverflow.com/questions/405208/… 我是这个问题的 OP。
    • 我以前读过这个问题,这是一个很好的问题。答案是你不能对技术进行版权保护,只有代码本身。此外,FSF 被强烈建议用于软件专利或算法和想法可以有效受版权保护并限制其使用的想法。
    【解决方案2】:

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

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

    【讨论】:

    【解决方案3】:

    这是来自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);
    }
    

    【讨论】:

    • 是的,我也发现了这个。不过,这里没什么特别的,这可以解释可笑的速度差异。
    【解决方案4】:

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

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

    【讨论】:

      【解决方案5】:

      GNU libc 肯定使用 assembly 版本的 memchr()(在任何常见的 linux 发行版上)。这就是它如此之快的原因。

      例如,如果我们计算 11Gb 文件中的行数(如 "wc -l" 所做的那样),大约需要 2.5 秒,使用来自 GNU libc 的 assembly 版本的 memchr()。但是,如果我们将 memchr() 程序集调用替换为来自 FreeBSD 的 memchr() C implementation - 速度将降低到 30 秒。

      这等于只用一个比较一个字符的while循环替换memchr()。

      【讨论】:

        猜你喜欢
        • 2021-12-09
        • 2019-04-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-06-23
        • 2020-11-06
        相关资源
        最近更新 更多