2013-06-21 31 views
4

<cstdio>头文件在C++中包含的功能与<stdio.h>相同,但放在std命名空间中?mingw-w64:慢速sprintf在<cstdio>

我在使用mingw-w64编译的程序中遇到了奇怪的效率问题,这比在Linux上慢了十多倍。经过一些测试后,我发现问题出在sprintf

然后我做了如下试验:

#include <stdio.h> 
// #include <cstdio> 
// using std::sprintf; 

int main() { 
    int i; 
    for (i = 0; i < 500000; i++){ 
    char x[100]; 
    sprintf(x, "x%dx%dx", i, i<<2); 
    } 
} 

<stdio.h>编译它是快15倍,然后使用<cstdio>。这里是时机:

$ time ./stdio 

real 0m0.557s 
user 0m0.046s 
sys  0m0.046s 

$ time ./cstdio 

real 0m7.465s 
user 0m0.031s 
sys  0m0.077s 

$ g++ --version 
g++.exe (rubenvb-4.8-stdthread) 4.8.1 20130324 (prerelease) 
Copyright (C) 2013 Free Software Foundation, Inc. 
This is free software; see the source for copying conditions. There is NO 
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 

更新1: 我有不同的MinGW-W64版本(rubenvb,drangon,和MinGW建造)进一步定时,并且发现使用<cstdio>定时4.x的所有32位版本秒和64位版本7.x〜8.x秒。而所有使用<stdio.h>的版本都在0.4〜0.6秒左右。

更新2: 我拆开在gdb的主要功能,并发现只有一行的区别:在<stdio.h>版本调用callq 0x4077c0 <sprintf><cstdio>版本调用callq 0x407990 <_Z7sprintfPcPKcz>

sprintf包含:

0x00000000004077c0 <+0>: jmpq *0x7c6e(%rip) # 0x40f434 <__imp_sprintf> 
0x00000000004077c6 <+6>: nop 
0x00000000004077c7 <+7>: nop 

__imp_sprintf我杀入内线msvcrt.dllsprinf

_Z7sprintfPcPKcz包含一些MinGW的代码:

0x0000000000407990 <+0>:  push %rbp 
0x0000000000407991 <+1>:  push %rbx 
0x0000000000407992 <+2>:  sub $0x38,%rsp 
0x0000000000407996 <+6>:  lea 0x80(%rsp),%rbp 
0x000000000040799e <+14>: mov %rcx,-0x30(%rbp) 
0x00000000004079a2 <+18>: mov %r8,-0x20(%rbp) 
0x00000000004079a6 <+22>: mov %r9,-0x18(%rbp) 
0x00000000004079aa <+26>: mov %rdx,-0x28(%rbp) 
0x00000000004079ae <+30>: lea -0x20(%rbp),%rax 
0x00000000004079b2 <+34>: mov %rax,-0x58(%rbp) 
0x00000000004079b6 <+38>: mov -0x58(%rbp),%rdx 
0x00000000004079ba <+42>: mov -0x28(%rbp),%rax 
0x00000000004079be <+46>: mov %rdx,%r8 
0x00000000004079c1 <+49>: mov %rax,%rdx 
0x00000000004079c4 <+52>: mov -0x30(%rbp),%rcx 
0x00000000004079c8 <+56>: callq 0x402c40 <__mingw_vsprintf> 
0x00000000004079cd <+61>: mov %eax,%ebx 
0x00000000004079cf <+63>: mov %ebx,%eax 
0x00000000004079d1 <+65>: add $0x38,%rsp 
0x00000000004079d5 <+69>: pop %rbx 
0x00000000004079d6 <+70>: pop %rbp 

为什么cstdio使用不同的(和慢得多)的功能?

+0

是,'的printf()'是缓慢的。抱歉。 (我并不觉得它在Linux上比在Windows上表现更好。) – 2013-06-21 13:25:17

+1

我的问题是:为什么'cstdio'慢,但在'stdio.h'中快。 –

+0

你能试几次运行测试吗? 'sprint'和'std :: sprintf'是[完全相同的函数](http://gcc.gnu.org/onlinedocs/gcc-4.6.2/libstdc++/api/a00832_source.html)。 –

回答

3

libstdC++在构建期间确定了__USE_MINGW_ANSI_STDIOconfig/os/mingw32-w64/os_defines.h),它将打开mingw sprintf包装器。正如@Michael Burr所指出的那样,这些包装对C99/GNU99兼容。

你的测试没有定义__USE_MINGW_ANSI_STDIO,因此你不会得到与stdio.h包装。但是由于它是在构建libstdC++时定义的,因此您可以使用cstdio来获取它。 如果您在包含stdio.h之前自己定义它,您将再次获得包装。

所以你确实得到了不同的实现,并且cstdio std::sprintf不一定与stdio.h sprintf相同,至少在涉及到mingw时是这样。

这是一个测试。第一源:

#ifdef USE_STDIO 
    #include <stdio.h> 
#else 
    #include <cstdio> 
    using std::sprintf; 
#endif 

int main() { 
    int i; 
    for (i = 0; i < 500000; i++){ 
    char x[100]; 
    sprintf(x, "x%dx%dx", i, i<<2); 
    } 
} 

结果:

$ g++ -o test_cstdio.exe test.cc 
$ g++ -o test_stdio.exe -DUSE_STDIO test.cc 
$ g++ -o test_stdio_wrap.exe -DUSE_STDIO -D__USE_MINGW_ANSI_STDIO test.cc 

$ for x in test_*.exe; do (echo $x; objdump -d $x | grep sprintf; echo); done 
test_cstdio.exe 
    40154a: e8 41 64 00 00   callq 407990 <_Z7sprintfPcPKcz> 
0000000000402c40 <__mingw_vsprintf>: 
0000000000407990 <_Z7sprintfPcPKcz>: 
    4079c8: e8 73 b2 ff ff   callq 402c40 <__mingw_vsprintf> 

test_stdio.exe 
    40154a: e8 71 62 00 00   callq 4077c0 <sprintf> 
00000000004077c0 <sprintf>: 
    4077c0: ff 25 6e 6c 00 00  jmpq *0x6c6e(%rip)  # 40e434 <__imp_sprintf> 

test_stdio_wrap.exe 
    40154a: e8 41 64 00 00   callq 407990 <_Z7sprintfPcPKcz> 
0000000000402c40 <__mingw_vsprintf>: 
0000000000407990 <_Z7sprintfPcPKcz>: 
    4079c8: e8 73 b2 ff ff   callq 402c40 <__mingw_vsprintf>