%20%E3%81%8C%20getc()%20%E3%82%88%E3%82%8A%E9%81%85%E3%81%84%E3%81%AE%E3%81%AF%E3%81%AA%E3%81%9C%E3%81%A7%E3%81%99%E3%81%8B%3F%20.png)
はなぜread
よりも遅いのでしょうかgetc
?
たとえば、次のようになります。
for (;;) {
chr++;
amr=read(file1, &wc1, 1);
amr2=read(file2, &wc2, 1);
if (wc1 == wc2) {
if (wc1 == '\n')
line++;
if (amr == 0) {
if (eflg)
return (1);
return (0);
}
continue;
}
それよりも遅いです:
for (;;) {
chr++;
c1 = getc(file1);
c2 = getc(file2);
if (c1 == c2) {
if (c1 == '\n')
line++;
if (c1 == EOF) {
if (eflg)
return (1);
return (0);
}
continue;
}
getc
システムコールを使用するのにread
、なぜ遅くなるのでしょうか?
答え1
getc() は、読み取ったデータをバッファリングしてから返すため、 の呼び出しがgetc()
の呼び出しになるとは限りませんread()
。read()
はシステム コールであり、カーネルが実行する操作が多いため、通常の関数呼び出しよりも完了までに時間がかかります。カーネル空間に入ると、スタックが変更され、すべてのコンテキストが保存され、マスクする割り込みが処理されます。そして、それが完了すると、コンテキストと割り込みが復元され、ユーザー空間のスタックが元に戻ります... すでにバッファリングされたデータが使用可能な場合は、重要なオーバーヘッドが節約されるため、getc() が推奨されます。
答え2
結局、ディスクからの読み取りはブロック指向であるという事実に行き着きます。ディスクから 1 バイトを読み取るために、ハードウェアはブロック (512 または 1024 などの数) のバイトを読み取り、それをすべてバッファリングしてカーネルに渡します。ファイルのブロック 0 からファイルのバイト 0 を読み取り、何らかの作業を行ってから、ファイルのバイト 1 を読み取ると、カーネルはおそらくファイルのブロック 0 を再度読み取ることになります。バイト 2 についても同じこと、バイト 3 についても同じこと。はい、カーネルとディスク ドライブ自体の両方でキャッシュが行われる可能性がありますが、カーネルは多くのプロセスを処理するため、そうではないかもしれません。
各read()
呼び出しでは、CPU をユーザー状態からカーネル状態に変更する必要もあります。少なくとも、メモリ マッピングは変更されます。おそらく、他にもそれほど明白ではない多くのことが発生します。これにも時間がかかることがあります。
システムread()
コールは CPU の状態を変更し、ディスク I/O を伴う可能性があります。 はgetc()
ディスク ブロック全体 (またはそれ以上) をユーザー空間にバッファリングできるため、 を 512 回呼び出すと、getc()
カーネルは 1 つのディスク ブロックを読み取り、状態が 1 回変更されます。 を調べると、またはディスク上のファイルへのシステムコールの効率的な (ディスク ブロックの倍数) サイズになるstdio.h
定数のマクロが見つかります。BUFSIZ
read()
write()