(おそらく)libparquet / libarrowのStreamWriterを使用して定期的にファイルに書き込むプロセスがあります(〜65 MBごとに書く必要があります)。たとえば、シェルを介してファイルを照会するときにwatch du -sh {file_name}
サイズが増加しないようです。しかし、プロセスが完了すると、ファイルが完全に作成されたように見えます。
ここで何が起こっているのでしょうか?プロセスがファイルを開いて終了するまで書き続けたと推測しますが、ファイルの実際のサイズはいつユーザー(またはOS)に反映されますか?ファイルが最終的に閉じられてからですか?
たとえば、Pythonがファイルに繰り返し書き込むときにシェルを介してwith open('file.txt','w') as fp
またはシェルからファイルを照会すると、ファイルの実際のサイズが反映されます。このPythonはちょうど背後に追加のステップを追加するのですか?ls
du
答え1
プログラムがどの方法を使用するかを指定しないと、I / Oを実行する方法が多く、その過程で多くの問題が発生する可能性があるため、答えは「非常に高速またはまったくそうではありません」です。また、さまざまなオペレーティングシステムやファイルシステムによって状況が複雑になる可能性があるため、ここでは説明しません。
一部のシステムでは、以下に示すテストプログラムをコンパイルするためにランダムなマジック文字列を定義する必要があります。たとえば、
feature_test_macros(7)
一部のLinuxバージョンを参照してください。現在、多くの(すべての人ではありませんが…)提供するC99コンパイラが必要な場合があります。
書く
write(2)
とても簡単です。バッファリングが不足しており、このコードで何も起こらないので、この呼び出しの出力はスペクトルのより早い端に表示されるべきです(すべてがうまくいく場合)。これらのスクリプトをktrace
他の同様
strace
のシステムコール追跡ユーティリティで実行することは教育的かもしれません。
// write.c -- `rm out; make write && ./write > out`
#include <stdio.h>
#include <unistd.h>
int main(void) {
char buf[] = "this is some output\n";
//while(1) {
write(STDOUT_FILENO, buf, sizeof(buf) / sizeof(char));
//}
return 0;
}
より多くの出力のために無限ループのコメントを外し、sleep(3)
「ファイルシステムは何を表示しますか?」テストに役立つように遅くする呼び出しを追加します。
バッファリング...バッファリング...バッファリング...
実際のプログラム(TM)はバッファリングされたI / Oを使用することができ、他の作業(膨張)が発生する可能性があります。ここではバッファリングについてのみ説明します。まず、出力をまったくエクスポートしないプログラム:
// never.c -- `rm out; make never && ./never > out`
#include <stdio.h>
#include <unistd.h>
int main(void) {
setvbuf(stdout, 0, _IOFBF, 0);
fputs("x", stdout);
_exit(0);
}
これは完全なバッファリングを選択しますが、何もフラッシュせずに終了します。これを達成するには他にも多くの方法があります。たとえば、人々はkill -9
segfaultsなどに興味があります。デュアルプロセッサLinuxのように、システムが非常に、非常に、非常に忙しい場合、システムのCPU負荷が約5,000の場合、通常は高速出力が「ほとんどなし」に変わることがあります。
ここでの簡単な修正は、ファイルシステムへの遅延出力をシミュレートする_exit
前に呼び出しexit
を追加するように変更し、バッファサイズを埋めるために出力を遅延させるためにスリープモードを含む無限ループを追加することです。sleep
それにもかかわらず、バッファリングによるファイルシステムの出力サイズは、プログラムの書き込みサイズよりも遅くなる可能性があります。
一時ファイル名の変更
もう1つの方法は、一時ファイルに書き込んでから、I / Oが完了したら一時ファイルの名前を物理ファイルに変更することです。これにより、リーダーが半分だけ作成された出力ファイルを表示する問題を回避できます。この場合、出力ファイルは呼び出し後にのみ新しいサイズを反映しますrename(2)
。
// rename.c -- `rm out*; make rename && ./rename & sleep 1; ls -l out*`
#include <err.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main(void) {
char *tmpbuf = strdup("out.XXXXXXXXXX");
char *tmpfile = mktemp(tmpbuf);
if (!tmpfile) err(1, "mktemp failed");
FILE *fh = fopen(tmpfile, "w");
if (!fh) err(1, "fopen failed");
fputs("this is some renamed output\n", fh);
fclose(fh);
sleep(9);
rename(tmpfile, "out");
//free(tmpbuf);
return 0;
}
ここでout
、ファイルは約9秒後に新しいサイズを反映する必要があります。あるいは、システムが非常に忙しい場合は、非常に多くの秒が残ります。または、名前の変更が完了する前に何も起こらないようにしてください。
メモリマップ
I / Oを実行するもう1つの方法は、メモリマッピングを使用することです。ただし、この動作を変更するフラグがある可能性がありますが、バッファリングの兆候は表示されません。
// mmap.c -- `make mmap && ./mmap & sleep 1; od -bc out`
#include <sys/mman.h>
#include <err.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main(void) {
char buf[] = "this is some mmap'd output\n";
size_t buflen = sizeof(buf) / sizeof(char);
int fd = open("out", O_RDWR | O_CREAT, 0666);
ftruncate(fd, buflen);
if (fd < 0) err(1, "open failed");
void *ptr = mmap(0, buflen, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (ptr == MAP_FAILED) err(1, "mmap failed");
sleep(9);
memcpy(ptr, buf, buflen);
munmap(ptr, buflen);
close(fd);
return 0;
}
I / Oが完了したら、メモリマップされたファイルの名前を一時ファイルから予想ファイルに変更できます(前のヒントを参照)。これには妥当な理由があるかもしれません。
その他
上記とは異なるシステムコールを使用する他の形式のI / Oがあります。strace
何を探すべきかを知っていれば、その内容が明らかになります。ファイルシステムの出力表示を遅らせるバッファリングがあるかもしれません。簡単なスクリプトを書くと、呼び出しがどのように機能するかを示すのに役立ちます。これは、より大きなプログラムで何を見つけるべきかを示すのに役立ちますstrace
。文書があることを願っています。
より高く、より深く積み上げる
標準ライブラリの上に構築された言語は、オペレーティングシステムによって提供されることに加えて独自のバッファを持つことができますが、通常ビットをディスクに転送しようとする呼び出しが1つ以上ある可能性がありますflush
。ユーザーができるようにデフォルトの動作を調整する方法です。または。一部の言語またはプログラミングガイドでは、オペレーティングシステムとは異なるデフォルト値を設定できます。バッファリングされた出力は、ファイルシステムに到達するのに遅延があるか、名前変更トリックが使用されている可能性があります。(finish-output)
sync
setvbuf(3)