Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/63.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/performance/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
为什么glibc';在Linux上,s sscanf比fscanf慢得多?_C_Performance_Glibc_Scanf - Fatal编程技术网

为什么glibc';在Linux上,s sscanf比fscanf慢得多?

为什么glibc';在Linux上,s sscanf比fscanf慢得多?,c,performance,glibc,scanf,C,Performance,Glibc,Scanf,我在x86_64 Linux上使用GCC 4.8和glibc 2.19 在使用不同的输入法时,我比较了fscanf和sscanf。具体来说,我会直接在标准输入上使用fscanf: char s[128]; int n; while (fscanf(stdin, "%127s %d", s, &n) == 2) { } 或者我会先将整个输入读入缓冲区,然后使用sscanf遍历缓冲区。(将所有内容读入缓冲区只需很少的时间。) 令我惊讶的是,fscanf版本的速度要快得多。例如,使用fsc

我在x86_64 Linux上使用GCC 4.8和glibc 2.19

在使用不同的输入法时,我比较了
fscanf
sscanf
。具体来说,我会直接在标准输入上使用
fscanf

char s[128]; int n;

while (fscanf(stdin, "%127s %d", s, &n) == 2) { }
或者我会先将整个输入读入缓冲区,然后使用
sscanf
遍历缓冲区。(将所有内容读入缓冲区只需很少的时间。)

令我惊讶的是,
fscanf
版本的速度要快得多。例如,使用
fscanf
处理数万行需要这么长的时间:

10000 0.003927487秒经过的时间
20000 0.006860206秒经过的时间
30000 0.007933329秒经过的时间
40000 0.012881912秒经过时间
50000 0.013516816秒经过的时间
60000 0.015670432秒经过时间
70000 0.017393129秒经过的时间
80000 0.019837480秒经过的时间
90000.023925753秒经过的时间
现在与
sscanf
相同:

10000 0.035864643秒
20000 0.127150772秒经过的时间
30000 0.319828373秒经过的时间
40000 0.611551668秒经过的时间
50000 0.919187459秒经过的时间
60000 1.327831544秒经过的时间
70000 1.809843039秒经过的时间
80000 2.354809588秒经过的时间
90000 2.970678416秒经过的时间
我用谷歌性能工具来衡量这一点。例如,对于50000条线路,
fscanf
代码需要大约50M的循环,而
sscanf
代码需要大约3300M的循环。因此,我用
perf-record
/
perf-report
分析了排名靠前的呼叫站点。使用
fscanf

35.26%xf libc-2.19.so[.]\u IO\u vfscanf
23.91%xf[kernel.kallsyms][k]0xffffffff8104f45a
8.93%xf libc-2.19.so[.]\u int\u malloc
使用
sscanf

98.22%xs libc-2.19.so[.]rawmemchr
0.68%xs-libc-2.19.so[.]\u IO\u vfscanf
0.38%xs[kernel.kallsyms][k]0xffffffff8104f45a
因此,几乎所有使用
sscanf
的时间都花在
rawmemchr
上!为什么会这样?
fscanf
代码如何避免这种成本

我试着搜索这个,但我能想到的最好的方法是锁定
realloc
调用,我认为这在这里不适用。我还认为,
fscanf
具有更好的内存局部性(反复使用相同的缓冲区),但这并不能产生如此大的差异


有人对这种奇怪的差异有什么见解吗?

看起来glibc的
sscanf()
在做任何其他事情之前先扫描源字符串的长度

sscanf()
(在
stdiocommon/sscanf.c
中)本质上是对
\u IO\u vsscanf()
(在
libio/iovsscanf.c
中)调用的包装。
\u IO\u vsscanf()
做的第一件事是通过调用
\u IO\u str\u init\u static\u internal()
(在
libio/strops.c
中)来初始化它自己的
\u IO\u strfile
结构,如果没有提供,它将计算字符串的长度。

sscanf()将传入的字符串转换为
\u IO\u文件*
,使字符串看起来像一个“文件”。因此,相同的内部_IO_vfscanf()可用于字符串和文件*

然而,作为转换的一部分,在一个_IO_str_init_static_internal()函数中完成,它调用
u rawmemchr(ptr,“\0”)
本质上是对输入字符串的strlen()调用。这种转换在每次调用sscanf()时都会完成,因为您的输入缓冲区相当大,它将花费相当多的时间来计算输入字符串的长度


使用fmemopen()和使用fscanf()从输入字符串创建文件*可能是另一种选择。

完整的代码:,我很难找到
\u IO\u vfscanf
的源代码。是我能找到的最好的,但这不一定是glibc 2.19。显示循环处理-看起来你有一个循环。@MichaelBurr:我链接了测试代码,并在问题中发布了循环。您认为
sscanf
每次都会扫描到字符串的末尾吗?这将与存储在
b
中的值相矛盾,该值具有预期值(即每次调用消耗一行输入)。@MichaelBurr:事实上,我认为Michael Burr是对的,似乎
sscanf
正在搜索整个文件中的尾随空,然后解析出您想要的三个变量。看看上面的例子,我建议针对glibc提交一份bug报告。通过使
sscanf
提供的虚拟
文件
使用不需要预先了解字符串长度的自定义操作,原则上可以解决此问题。实际上,我们在musl libc中的实现避免了这个问题,所以我知道这是可能的。:-)@R:我以前从未听说过musl——谢谢你指出它!
char s[128]; int n;
char const * p = my_data;

for (int b; sscanf(p, "%127s %d%n", s, &n, &b) == 2; p += b) { }