Dav*_*ave 19 c performance locking stdio mingw32
我写过一本表现不佳的翻译,几周来一直在苦苦挣扎.在下面简单的bechmark
#include<stdio.h>
int main()
{
int x;
char buf[2048];
FILE *test = fopen("test.out", "wb");
setvbuf(test, buf, _IOFBF, sizeof buf);
for(x=0;x<1024*1024; x++)
fprintf(test, "%04d", x);
fclose(test);
return 0
}
Run Code Online (Sandbox Code Playgroud)
我们看到以下结果
bash-3.1$ gcc -O2 -static test.c -o test
bash-3.1$ time ./test
real 0m0.334s
user 0m0.015s
sys 0m0.016s
Run Code Online (Sandbox Code Playgroud)
正如您所看到的,在添加"-std = c99"标志的那一刻,性能崩溃了:
bash-3.1$ gcc -O2 -static -std=c99 test.c -o test
bash-3.1$ time ./test
real 0m2.477s
user 0m0.015s
sys 0m0.000s
Run Code Online (Sandbox Code Playgroud)
我正在使用的编译器是gcc 4.6.2 mingw32.
生成的文件大约是12M,所以这两者之间的差异大约为21MB/s.
运行diff
显示生成的文件是相同的.
我认为这与文件锁定有关fprintf
,程序大量使用,但我无法找到在C99版本中关闭它的方法.
我尝试flockfile
了在程序开始时使用的流,并funlockfile
在最后使用了相应的流,但是遇到了关于隐式声明的编译器错误,以及声称对这些函数的未定义引用的链接器错误.
是否可以对此问题进行另一种解释,更重要的是,有没有办法在Windows上使用C99而无需支付如此巨大的性能价格?
在查看这些选项生成的代码之后,它看起来像在慢速版本中,mingw坚持以下内容:
_fprintf:
LFB0:
.cfi_startproc
subl $28, %esp
.cfi_def_cfa_offset 32
leal 40(%esp), %eax
movl %eax, 8(%esp)
movl 36(%esp), %eax
movl %eax, 4(%esp)
movl 32(%esp), %eax
movl %eax, (%esp)
call ___mingw_vfprintf
addl $28, %esp
.cfi_def_cfa_offset 4
ret
.cfi_endproc
Run Code Online (Sandbox Code Playgroud)
在快速版本中,这根本不存在; 否则,两者完全相同.我认为__mingw_vfprintf
这里似乎是慢速的,但我不知道它需要模仿的行为使它变得如此缓慢.
Dav*_*ave 11
在深入挖掘源代码后,我发现为什么MinGW函数非常慢:
在[v,f,s]printf
MinGW 的开头,有一些看似无辜的初始化代码:
__pformat_t stream = {
dest, /* output goes to here */
flags &= PFORMAT_TO_FILE | PFORMAT_NOLIMIT, /* only these valid initially */
PFORMAT_IGNORE, /* no field width yet */
PFORMAT_IGNORE, /* nor any precision spec */
PFORMAT_RPINIT, /* radix point uninitialised */
(wchar_t)(0), /* leave it unspecified */
0, /* zero output char count */
max, /* establish output limit */
PFORMAT_MINEXP /* exponent chars preferred */
};
Run Code Online (Sandbox Code Playgroud)
但是,PFORMAT_MINEXP
它似乎不是:
#ifdef _WIN32
# define PFORMAT_MINEXP __pformat_exponent_digits()
# ifndef _TWO_DIGIT_EXPONENT
# define _get_output_format() 0
# define _TWO_DIGIT_EXPONENT 1
# endif
static __inline__ __attribute__((__always_inline__))
int __pformat_exponent_digits( void )
{
char *exponent_digits = getenv( "PRINTF_EXPONENT_DIGITS" );
return ((exponent_digits != NULL) && ((unsigned)(*exponent_digits - '0') < 3))
|| (_get_output_format() & _TWO_DIGIT_EXPONENT)
? 2
: 3
;
}
Run Code Online (Sandbox Code Playgroud)
每当我想要打印时,这就会被调用,并且getenv
在窗口上一定不能很快.用a替换define定义2
将运行时恢复到应该的位置.
因此,答案归结为:当使用-std=c99
或任何ANSI兼容模式时,MinGW使用自己的模式切换CRT运行时.通常,这不会是一个问题,但MinGW lib有一个错误,它的格式化功能远远超出了任何可以想象的范围.
使用-std=c99
禁用所有GNU扩展.
使用GNU扩展和优化,您fprintf(test, "B")
可能会被替换为fputc('B', test)
请注意,此答案已过时,请参阅/sf/answers/978149371/和/sf/answers/978175341/