id:eel3:20151102:1446476928 のhcasl、TDM-GCC 4.8.1でビルドしたC言語/C++版バイナリを手元のCygwin(64bit)で実行すると激遅なの。
$ make -f Makefile_mingw g++ -finput-charset=cp932 -Wall -std=c++11 -pedantic hcasl.cpp -o hcasl $ time perl -e 'print("a" x 1000000)' | ./hcasl >/dev/null real 0m42.853s user 0m0.015s sys 0m0.077s $ _
ところが、gprofでプロファイルをとってみても、どこも遅くない。不思議!
頭を抱えたのだが、山勘でリダイレクト先を/dev/nullから普通のファイルに変更したら速くなった。
$ time perl -e 'print("a" x 1000000)' | ./hcasl >zzzz.txt real 0m2.278s user 0m0.015s sys 0m0.030s $ _
しかし、さらに不思議なことに、他の言語による実装では、リダイレクト先を/dev/nullから普通のファイルに変更しても、実行速度はそれほど変化しなかった。
Cygwinのbashではなくコマンドプロンプトを使用して、リダイレクト先をNULにした場合にも、実行速度は激遅だった。/dev/null(つまりCygwin)に起因する問題ではなく、Windows自体に契機となりそうな何かがありそうだ。
Windows側だけでなくTDM-GCCにも何か問題があるのだろうか? こんな小ツールを作り、TDM-GCCでビルドしたバイナリと、Visual Studio 2013でビルドしたバイナリとで、実行時間を比較してみた。
/* test.c */ #include <stdio.h> #include <stdlib.h> int main(void) { int c; while ((c = fgetc(stdin)) != EOF) fputc(c, stdout); return EXIT_SUCCESS; }
$ time ./test.exe <big-file.txt >/dev/null
TDM-GCCとVisual Studio 2013、どちらも同じくらい遅かった。えっ?
Perl版hcaslで作成した8.58MBのテキストを入力として計測した値がこちら。
コンパイラ | realの値 |
TDM-GCC | 0m40.950s |
Visual Studio 2013 | 0m41.824s |
どちらも40秒強かかっている。C言語版hcaslよりわずかに速い程度だ。
ここでさらに山勘で、標準入出力をバイナリモードにしてみた。
#include <errno.h> #include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <io.h> #ifndef STDIN_FILENO # define STDIN_FILENO 0 #endif #ifndef STDOUT_FILENO # define STDOUT_FILENO 1 #endif int main(void) { int c; errno = 0; if (_setmode(STDIN_FILENO, O_BINARY) == -1) { perror("_setmode"); return EXIT_FAILURE; } errno = 0; if (_setmode(STDOUT_FILENO, O_BINARY) == -1) { perror("_setmode"); return EXIT_FAILURE; } while ((c = fgetc(stdin)) != EOF) fputc(c, stdout); return EXIT_SUCCESS; }
TDM-GCCとVisual Studio 2013、どちらも劇的に速くなった!
先ほどと同じ条件で計測しなおした値がこちら。
コンパイラ | realの値 |
TDM-GCC | 0m2.543s |
Visual Studio 2013 | 0m2.465s |
40秒強から2.5秒へと、大幅に高速になっている。
テキストモードとバイナリモードの違いといえば、改行コードの変換をするか否かぐらいしか思い浮かばないが……C言語版とC++版のhcaslが遅いのは、Windowsのテキストモードの入出力で実行される改行コードの変換に起因する、ということだろうか?
しかし、hcaslでは、テキストモードでもリダイレクト先がファイルなら高速だった。つまり、テキストモードでコマンドプロンプトのNULないしCygwinの/dev/nullにリダイレクトした場合だけ遅い? なんで?
回避策は分かったが、謎が残ってしまった。