mingw-w64: медленный спринт в & lt; cstdio & gt;

В том, что <cstdio> Заголовок в C ++ содержит те же функции, что и <stdio.h> но положить в std Пространство имен?

У меня были странные проблемы с эффективностью в моей программе, скомпилированной с 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-build) и обнаружил, что все 32-битные версии используют <cstdio> время 4.x секунд и 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 Я достиг sprinf внутри msvcrt.dll,

_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 использовать другую (и гораздо более медленную) функцию?

4

Решение

libstdc ++ определяет __USE_MINGW_ANSI_STDIO во время сборки (config/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>
5

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]