web-dev-qa-db-ja.com

freadは実際にどのように機能しますか?

freadの宣言は次のとおりです。

_size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
_

問題は、そのような2つのfreadの呼び出しの読み取りパフォーマンスに違いはありますか?

_char a[1000];
_
  1. fread(a, 1, 1000, stdin);
  2. fread(a, 1000, 1, stdin);

毎回_1000_バイト一度にを読み取りますか?

71
Beginner

パフォーマンスに違いがある場合とない場合があります。セマンティクスには違いがあります。

_fread(a, 1, 1000, stdin);
_

それぞれが1バイト長の1000個のデータ要素を読み取ろうとします。

_fread(a, 1000, 1, stdin);
_

1000バイト長の1つのデータ要素を読み取ろうとします。

fread()は、バイト数ではなく、読み取れたデータ要素の数を返すため、これらは異なります。 1000バイト全体を読み取る前にファイルの終わり(またはエラー状態)に達した場合、最初のバージョンは、読み取ったバイト数を正確に示す必要があります。 2番目は失敗し、0を返します。

実際には、おそらく1000バイトの読み取りを試行し、実際に読み取ったバイト数を示す下位レベルの関数を呼び出すだけです。大きな読み取りの場合、複数の低レベルの呼び出しを行う場合があります。 fread()によって返される値の計算は異なりますが、計算の費用は簡単です。

実装が、データを読み取ろうとする前に、読み取るのに十分なデータがないことを伝えることができる場合、違いがあるかもしれません。たとえば、900バイトのファイルから読み込んでいる場合、最初のバージョンは900バイトすべてを読み込んで900を返しますが、2番目のバージョンは何も読み込まなくてもかまいません。どちらの場合も、ファイル位置インジケータは文字が正常に読み取られた数、つまり900だけ進んでいます。

ただし、一般的には、必要な情報に基づいて呼び出す方法を選択する必要があります。部分的な読み取りがまったく何も読み取らないよりも良い場合、単一のデータ要素を読み取ります。部分的な読み取りが有用な場合は、小さなチャンクで読み取ります。

99
Keith Thompson

仕様 によると、この2つは実装によって異なる方法で処理される場合があります。

ファイルが1000バイト未満の場合、fread(a, 1, 1000, stdin)(各1バイトの1000要素を読み取る)は、EOFまですべてのバイトをコピーします。一方、aに格納されているfread(a, 1000, 1, stdin)(1 1000バイト要素の読み取り)の結果は指定されていません。これは、「最初の」 )1000バイトの要素。

もちろん、一部の実装では、「部分」要素を必要な数のバイトにコピーできます。

17
ArjunShankar

それが実装の詳細です。 glibcでは、基本的に(Ref http://sourceware.org/git/?p=glibc.git;a=blob;f=libioとして実装されているため、この2つのパフォーマンスは同じです。 /iofread.c ):

size_t fread (void* buf, size_t size, size_t count, FILE* f)
{
    size_t bytes_requested = size * count;
    size_t bytes_read = read(f->fd, buf, bytes_requested);
    return bytes_read / size;
}

およびPOSIX standardは、サイズsizeの完全なオブジェクトを毎回読み取る必要があることを保証しません。完全なオブジェクトを読み取ることができない場合(たとえば、stdinは999バイトしかありませんが、size == 1000)、ファイルは相互確定状態のままになります(C99§7.19.8.1/ 2)。

編集:POSIXに関する他の回答を参照してください。

13
kennytm

freadは、getcを内部的に呼び出します。 Minixが呼び出される回数getcは単純に_size*nmemb_であるため、getcが呼び出される回数はに依存しますこれら2つの製品。したがって、fread(a, 1, 1000, stdin)fread(a, 1000, 1, stdin)の両方がgetc1000=(1000*1)回実行されます。 Minixの fread の単純な実装を次に示します。

_size_t fread(void *ptr, size_t size, size_t nmemb, register FILE *stream){
register char *cp = ptr;
register int c;
size_t ndone = 0;
register size_t s;

if (size)
    while ( ndone < nmemb ) {
    s = size;
    do {
        if ((c = getc(stream)) != EOF)
            *cp++ = c;
        else
            return ndone;
    } while (--s);
    ndone++;
}

return ndone;
}
_
3
Neel Basu

パフォーマンスに違いはないかもしれませんが、それらの呼び出しは同じではありません。

  • freadは読み取られた要素の数を返すため、これらの呼び出しは異なる値を返します。
  • 要素を完全に読み取れない場合、その値は不定です。

エラーが発生した場合、ストリームのファイル位置インジケータの結果の値は不定です。部分的な要素が読み取られる場合、その値は不定です。 (ISO/IEC 9899:TC2 7.19.8.1)

glibcの実装 には大きな違いはありません。これは、要素サイズに要素数を掛けて、読み取るバイト数を決定し、読み取った量を最終的にメンバーサイズで除算します。ただし、要素サイズ1を指定するバージョンでは、常に正しいバイト数が読み取られます。ただし、特定のサイズの要素を完全に読み取るだけでよい場合は、他のフォームを使用すると、除算を行う必要がなくなります。

3
Artefacto

もう1つの文形式 http://pubs.opengroup.org/onlinepubs/000095399/functions/fread.html は注目に値します

Fread()関数は、streamが指すストリームから、サイズがバイト単位のサイズで指定されるnitems要素まで、ptrが指す配列に読み込みます。 各オブジェクトについて、サイズの呼び出しはfgetc()関数に対して行われ、結果は、符号なしcharの配列に読み取られた順序で正確に格納されますオブジェクトのオーバーレイ。

どちらの場合でも、fgetc()によってデータにアクセスします...!

1
Jeegar Patel

ここで答えを明確にしたかった。 freadはバッファIOを実行します。 freadが使用する実際の読み取りブロックサイズは、使用されているC実装によって決まります。

最新のCライブラリはすべて、2つの呼び出しで同じパフォーマンスを発揮します。

fread(a, 1, 1000, file);
fread(a, 1000, 1, file);

次のようなものでも:

for (int i=0; i<1000; i++)
  a[i] = fgetc(file)

同じディスクアクセスパターンになるはずですが、fgetcは標準のcライブラリへの呼び出しが多くなり、場合によっては最適化されていない追加のシークを実行するためにディスクが必要になるため、遅くなります。

2つの形式のfreadの違いに戻りましょう。前者は、読み取られた実際のバイト数を返します。後者は、ファイルサイズが1000未満の場合は0を返し、それ以外の場合は1を返します。どちらの場合も、バッファには同じデータ、つまりファイルのコンテンツが最大1000バイトで埋められます。

一般に、2番目のパラメーター(サイズ)を1に設定したままにして、読み取りバイト数を取得することをお勧めします。

0
Claris