c - 没有 "-std=c99"的巨大 fprintf 速度差异

标签 c performance locking stdio mingw32

数周以来,我一直在为我编写的一个表现不佳的译者而苦苦挣扎。 在以下简单的基准上

#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
}

我们看到如下结果

bash-3.1$ gcc -O2 -static test.c -o test
bash-3.1$ time ./test

real    0m0.334s
user    0m0.015s
sys     0m0.016s

如您所见,添加“-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

我使用的编译器是 gcc 4.6.2 mingw32。

生成的文件大小约为12M,因此两者相差约21MB/s。

运行 diff 显示生成的文件是相同的。

我假设这与 fprintf 中的文件锁定有关,该程序大量使用它,但我一直无法找到在 C99 版本中关闭它的方法.

我在程序开始时在我使用的流上尝试了 flockfile,并在末尾尝试了相应的 funlockfile,但是遇到了关于隐式声明的编译器错误,和链接器错误声明对这些函数的 undefined reference 。

是否可以对这个问题有另一种解释,更重要的是,有没有什么方法可以在 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 

在快速版本中,这根本不存在;否则,两者完全相同。我假设 __mingw_vfprintf 似乎是这里的 slowpoke,但我不知道它需要模拟什么行为才能让它变得如此缓慢。

最佳答案

在深入研究源代码之后,我发现了为什么 MinGW 函数如此慢:

在 MinGW 中的 [v,f,s]printf 开头,有一些看似无辜的初始化代码:

__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   */
};

但是,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
    ;
}

每次我想打印时都会被调用,Windows 上的 getenv 一定不会很快。用 2 替换该定义可将运行时恢复到应有的位置。


所以,答案归结为:当使用 -std=c99 或任何符合 ANSI 的模式时,MinGW 会切换 CRT 运行时。通常情况下,这不是问题,但 MinGW 库有一个错误,导致其格式化功能的速度降低到无法想象的程度。

关于c - 没有 "-std=c99"的巨大 fprintf 速度差异,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13970675/

相关文章:

c# - 将 IQueryable linq 查询转换为 IEnumerable<T> 会取消 linq 优化的工作方式吗?

c - 带锁的 C 读写 (POSIX)

c++ - 使用预处理器对 C/C++ 进行元编程

c - c 中的单链表数据结构 : linking a list

c - 消耗指针是什么意思?

android - 如果在自定义软键盘上快速键入,LinearLayout 会导致延迟

c - 如何使用 GLib 获取基于文本的用户输入

objective-c - Objective-C 运行时性能惩罚的细节

java - 什么锁是不可重入的,即如果在同一线程中获取而没有事先释放则阻塞?

static - 无法编辑的QListView