fwrite は内部で WriteFile を呼び出すので、私は常に WriteFile が fwrite よりも効率的であると考えてきましたが、次のテスト コードは、fwrite が WriteFile よりも大幅に高速であることを示しています。
fwrite には 2 ミリ秒のコストがかかりますが、WriteFile には 27000(FILE_ATTRIBUTE_NORMAL) が必要です。両方とも書き込み呼び出しのたびにフラッシュされます。FILE_FLAG_WRITE_THROUGH を指定して WriteFile を呼び出し、FlushFileBuffers(wfile) 行にコメントを付けると、WriteFile が高速になり、コストは 800 になります。
では、fwrite が WriteFile を呼び出しているのは本当ですか? 何がそんなに大きな違いを生んでいるのでしょうか? fwrite は内部でどのように機能しますか? fwrite よりも効率的に API を使用してデータをファイルに書き込むにはどうすればよいですか? (unbufferd、同期)。
#include <Windows.h>
#include <stdio.h>
#include <iostream>
int main() {
FILE* cfile = fopen("file1.txt", "w");
HANDLE wfile = CreateFile("file2.txt", GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS,
/*FILE_ATTRIBUTE_NORMAL*/FILE_FLAG_WRITE_THROUGH, NULL);
DWORD written = 0;
DWORD start_time, end_time;
char * text = "test message ha ha ha ha";
int size = strlen(text);
int times = 999;
start_time = timeGetTime();
for(int i = 0; i < times; ++i) {
fwrite(text, 1, size, cfile);
fflush(cfile);
}
end_time = timeGetTime();
std::cout << end_time - start_time << '\n';
start_time = timeGetTime();
for(int i = 0; i < times; ++i) {
WriteFile(wfile, text, size, &written, NULL);
//FlushFileBuffers(wfile);
}
end_time = timeGetTime();
std::cout << end_time - start_time << std::endl;
system("pause");
return 0;
}
更新: 回答ありがとうございます。回答は次のとおりです: VS directory\VS\crt\src\fflush.c を参照してください:
//fflush.c
int __cdecl _fflush_nolock (FILE *str) {
//irrelevant codes
if (str->_flag & _IOCOMMIT) {
return (_commit(_fileno(str)) ? EOF : 0);
}
return 0;
}
ここに _IOCOMMIT フラグがあります。それから ...\src\fdopen.c を参照してください。
FILE * __cdecl _tfdopen (int filedes, const _TSCHAR *mode) {
//irrelevant codes
while(*++mode && whileflag)
switch(*mode) {
//...
case _T('c'):
if (cnflag)
whileflag = 0;
else {
cnflag = 1;
fileflag |= _IOCOMMIT;
}
break;
//...
}
_tfopen は fopen によって内部的に呼び出されます。fopen のドキュメントを参照してください。
" モード: 'c'
関連付けられたファイル名のコミット フラグを有効にして、fflush または _flushall が呼び出された場合にファイル バッファの内容がディスクに直接書き込まれるようにします。
_commit 関数は最終的に FlushFileBuffers を呼び出します。
これらに加えて、少数のデータのみをファイルに書き込む場合(バッファサイズを超えない場合)、fflush なしで fwrite を実行すると、FlushFileBuffers を呼び出さなくても WriteFile の後にテキストが明らかに書き込まれないことがわかります。 、ファイルを開くと(プログラムがスリープ状態にある)、コンテンツが自動的にファイルに書き込まれます。これが、フラッシュについて混乱した理由の1つでした。この操作はOSによって行われる可能性があり、WriteFileはデータをシステムキャッシュにコピーし、そのファイル バッファは OS によって管理されるため、fflush() が実際のフラッシュなしで内部的にのみ WriteFile を呼び出すことは合理的です。システムは、ファイル ハンドルが閉じられたとき、またはこのファイルへの別の I/O アクセスが発生したときに、それらをフラッシュするタイミングを認識しています。そこで、ベンチマークを次のように変更しました。
start_time = timeGetTime();
for(int i = 0; i < times; ++i) {
fwrite(text, 1, size, cfile);
fflush(cfile);
}
end_time = timeGetTime();
std::cout << end_time - start_time << '\n';
start_time = timeGetTime();
for(int i = 0; i < times; ++i) {
WriteFile(wfile, text, size, &written, NULL);
}
end_time = timeGetTime();
std::cout << end_time - start_time << std::endl;
結果は times:99999 fwrite:217 WriteFile:171 です
結論として、API ファイルの書き込み操作を高速化するには、次のようにします。
FlushFileBuffers を明示的に呼び出さないでください。システム キャッシュ内のデータは、必要に応じてディスクにフラッシュされます。
API 呼び出しは単に memcpy よりも時間がかかるため、fwrite と同様に、WriteFile のバッファーを取得します。バッファーがいっぱいになったら、WriteFile を呼び出します。