背景:我正在尝试创建一个纯D语言实现,其功能大致相当于C's memchr但使用数组和索引而不是指针。原因是std.string将用于编译时功能评估。对于那些不熟悉w / D的人,如果满足某些限制,可以在编译时评估函数。一个限制是它们不能使用指针。另一个是他们不能调用C函数或使用内联汇编语言。让字符串库在编译时工作对于某些编译时代码生成非常有用。
问题:memchr如何以最快的速度运行?在Win32上,我使用简单循环在纯D中创建的任何东西,即使有明显的优化技术,例如禁用边界检查,循环展开等,也至少要慢2倍。有哪些非显而易见的技巧可用于像在字符串中查找字符一样简单吗?
答案 0 :(得分:12)
我建议看看GNU libc的来源。对于大多数函数,它将包含函数的通用优化C版本,以及尽可能多的支持体系结构的优化汇编语言版本,利用机器特定的技巧。
x86-64 SSE2 version将pcmpeqb
的结果一次性整合到整个缓存行数据(四个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)
一个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(),该循环将一个字符与另一个字符进行比较。