10

私が書いたゲームのコードをプロファイリングしていますが、次のスニペットが実行されるたびに 4kb のヒープ増加を引き起こす可能性があることを疑問に思っています (私は Xcode のヒープショット分析でプロファイリングしています):

u8 WorldManager::versionOfMap(FILE *file)
{
  char magic[4];
  u8 version;

  fread(magic, 4, 1, file); <-- this is the line
  fread(&version,1,1,file);
  fseek(file, 0, SEEK_SET);

  return version;
}

プロファイラーによると、強調表示された行mallocは、関数が呼び出されるたびに 4.00Kb のメモリを割り当てますが、メモリは決して解放されません。これは、コード周辺の他の呼び出しでも発生するようですfreadが、これが最も熱狂的なものでした。

私が見逃している些細なことはありますか?それは私が気にするべきではない内部的なものですか?

注: 私は iPhone でプロファイリングしており、リリース ( -O2) としてコンパイルされています。

4

1 に答える 1

3

あなたが説明していることが実際に起こっていて、コードに他の場所にバグがない場合、それは実装のバグだと思います。

おそらく、ファイルを閉じていない可能性が高いと思います。デバイスが非対話型の場合、Stdio ストリームはデフォルトでバッファリングを使用し、バッファはファイルが開かれたとき、または I/O が実行されたときに割り当てられます。バッファーは 1 つだけ割り当てる必要がありますが、ファイルを閉じるのを忘れると、バッファーが確実にリークする可能性があります。しかし、確かに、ファイルを閉じるとバッファが解放されます。によって返される値を確認することを忘れないでくださいfclose

ファイルを正しく閉じているという議論のために、コードにはこの問題を引き起こさないいくつかの他のニットがあると仮定しますが、とにかく言及します。

最初に、fread呼び出しはサイズ 4 のメンバーを 1 つ持つオブジェクトを読み取ります。実際には、サイズ 1 のメンバーを 4 つ持つオブジェクトがあります。つまり、数値引数freadが交換されます。これは、戻り値の意味のみが異なります (部分読み取りの場合は重要です)。

第 2 に、 への最初の呼び出しでfreadは のサイズが 1 として正しくハードコーディングされていますが (C では、これが「サイズ」の定義です)、 2 番目の への呼び出しでchar使用する方がスタイル的にはおそらく優れています。sizeof(u8)fread

これが本当にメモリ リークであるという考えが正しい解釈である場合 (そして他の場所にバグがない場合)、この特定のファイルの stdio バッファリングをオフにすることで問題を回避できる場合があります。

bool WorldManager::versionOfMap(FILE *file, bool *is_first_file_io, u8 *version)
{
  char magic[4];
  bool ok = false;
  if (*is_first_file_io) 
  {
    // we ignore failure of this call
    setvbuf(file, NULL, _IONBF, 0);
    *is_first_file_io = false;
  }

  if (sizeof(magic) == fread(magic, 1, sizeof(magic), file) 
      && 1 == fread(version, sizeof(*version), 1, file))
  {
      ok = true;
  }
  if (-1 == fseek(file, 0L, SEEK_SET))
  {
      return false;
  }
  else
  {
      return ok && 0 == memcmp(magic, EXPECTED_MAGIC, sizeof(magic));
  }
}

これが本当にバグであり、リークが本物であるという仮説を立てていたとしても、問題を示す可能な限り小さな例にコードを要約する価値は十分にあります。そうすることで真のバグが明らかになれば、あなたの勝ちです。それ以外の場合は、実装のバグを報告するための最小限の例が必要になります。

于 2012-04-14T19:56:41.753 に答える