19

私は、自分が書いたパフォーマンスの悪い翻訳者と何週間も格闘していました。次の簡単な基準について

#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が、暗黙の宣言に関するコンパイラ エラーと、これらの関数への未定義の参照を主張するリンカ エラーが発生しました。

この問題には別の説明があるのでしょうか? さらに重要なことに、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ではスローポークのように思われますが、エミュレートする必要がある動作が非常に遅くなる理由がわかりません。

4

4 に答える 4

12

ソースコードを掘り下げた後、MinGW 関数が非常に遅い理由を発見しました。

MinGWの a の先頭に、[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
    ;
}

これは、印刷するたびに呼び出されることになりgetenv、Windows では非常に高速である必要はありません。その定義を a に置き換えると2、ランタイムが本来あるべき場所に戻ります。


したがって、答えは次のようになります。-std=c99または ANSI 準拠のモードを使用する場合、MinGW は CRT ランタイムを独自のものに切り替えます。通常、これは問題になりませんが、MinGW lib にはバグがあり、フォーマット機能が想像を絶するほど遅くなりました。

于 2012-12-20T14:07:21.027 に答える
8

-std=c99すべての GNU 拡張機能を無効にします。

GNU 拡張機能と最適化fprintf(test, "B")により、おそらくあなたはfputc('B', test)

この回答は廃止されていることに注意してください。 https://stackoverflow.com/a/13973562/611560およびhttps://stackoverflow.com/a/13973933/611560を参照してください

于 2012-12-20T11:28:18.950 に答える
0

MinGW32 3.15 以降printf、Microsoft C ランタイム (CRT) にある関数の代わりに、準拠した関数を使用できます。新しいprintf関数は、厳密な ANSI、POSIX、および/または C99 モードでコンパイルするときに使用されます。

詳細については、mingw32 変更ログを参照してください。

高速(非準拠)機能__msvcrt_fprintf()を使用するために使用できます。

于 2012-12-20T13:46:22.453 に答える
0

アセンブラを検討した結果、低速バージョンは間違いなく GCC に基づいた MinGW の実装を使用しているように見えますが*printf()、高速バージョンは Microsoft の実装を使用していますmsvcrt.dll

現在、MS のものは、GCC のものが実装している多くの機能が不足しているためです。これらの一部は GNU 拡張ですが、その他の一部は C99 準拠用です。そして、あなたは使用しているので-std=c99、適合を要求しています。

しかし、なぜそんなに遅いのですか?1 つの要因はシンプルさです。MS バージョンははるかにシンプルであるため、些細なケースでもより高速に実行されることが期待されます。もう 1 つの要因は、Windows で実行していることです。そのため、MS バージョンは Unix の世界からコピーしたものよりも効率的であることが期待されます。

x10の係数を説明していますか?おそらくそうではありません...

あなたが試すことができる別のこと:

  • に置き換えfprintf()sprintf()、ファイルにまったく触れずにメモリ バッファーに出力します。次に、printfingfwrite()なしで試してみることができます。そうすれば、損失がデータのフォーマットにあるのか、.FILE
于 2012-12-20T13:31:38.820 に答える