4

私の会社の製品は、多くの認定されたLinuxハードウェア/ソフトウェア構成で動作します。歴史的に、使用されているコンパイラはGNUC++でした。この投稿の目的のために、バージョン3.2.3のベースラインについて考えてみましょう。これは、ソフトウェアがそのバージョンを通じて「期待どおりに機能した」ためです。

GNU C ++バージョン3.4.4を使用した、より新しい認定プラットフォームの導入により、これまでに見たことのないいくつかのパフォーマンスの問題が観察され始めました。少し掘り下げた後、エンジニアの1人がこのテストプログラムを思いつきました。

#include <fstream>
#include <iostream>

using namespace std;

class my_filebuf : public filebuf
{
public:

   my_filebuf() : filebuf(), d_underflows(0) {};
   virtual ~my_filebuf() {};

   virtual pos_type seekoff(off_type, ios_base::seekdir,
                            ios_base::openmode mode = ios_base::in | ios_base::out);

   virtual int_type underflow();

public:
   unsigned int d_underflows;
};

filebuf::pos_type my_filebuf::seekoff(
   off_type           off,
   ios_base::seekdir  way,
   ios_base::openmode mode
)
{
   return filebuf::seekoff(off, way, mode);
}

filebuf::int_type my_filebuf::underflow()
{
   d_underflows++;

   return filebuf::underflow();
}

int main()
{
   my_filebuf fb;
   fb.open("log", ios_base::in);
   if (!fb.is_open())
   {
      cerr << "need log file" << endl;
      return 1;
   }

   int count = 0;
   streampos pos = EOF;
   while (fb.sbumpc() != EOF)
   {
      count++;

      // calling pubseekoff(0, ios::cur) *forces* underflow
      pos = fb.pubseekoff(0, ios::cur);
   }

   cerr << "pos=" << pos << endl;
   cerr << "read chars=" << count << endl;
   cerr << "underflows=" << fb.d_underflows << endl;

   return 0;
}

約751KB文字のログファイルに対して実行しました。以前の構成では、次の結果が得られました。

$ buftest
pos=768058
read chars=768058
underflows=0

新しいバージョンでは、結果は次のようになります。

$ buftest
pos=768058
read chars=768058
underflows=768059

pubseekoff(0、ios :: cur)呼び出しをコメントアウトすると、過剰なunderflow()呼び出しがなくなります。したがって、明らかに、g ++の新しいバージョンでは、pubseekoff()を呼び出すとバッファが無効になり、underflow()が強制的に呼び出されます。

標準ドキュメントを読みましたが、pubseekoff()の言い回しは確かにあいまいです。たとえば、基になるファイルポインタの位置とgptr()の位置との関係は何ですか?underflow()の呼び出しの前または?それにもかかわらず、いわば、g++が「中流で馬を変えた」ことは苛立たしいことだと思います。さらに、一般的なseekoff()でバッファポインタを無効にする必要がある場合でも、ftell()と同等のものが必要なのはなぜですか

この振る舞いの変化につながった実装者間のディスカッションスレッドを誰かに教えてもらえますか?関連する選択とトレードオフについて簡潔に説明していますか?

追加クレジット

明らかに、私は自分が何をしているのか本当にわかりません。私は、オフセットが0で、seekdirがios :: curの場合に無効化をバイパスする方法があるかどうかを判断するために実験していましたが、移植性はありません。私は次のハックを思いつき、 filebufデータメンバー_M_fileに直接アクセスしました(これは私のマシンの3.4.4バージョンでのみコンパイルしたかったです):

int sc(0);
filebuf::pos_type my_filebuf::seekoff(
   off_type           off,
   ios_base::seekdir  way,
   ios_base::openmode mode
)
{
   if ((off == 0) && (way == ios::cur))
   {
      FILE *file =_M_file.file();
      pos_type pos = pos_type(ftell(file));

      sc++;
      if ((sc % 100) == 0) {
         cerr << "POS IS " << pos << endl;
      }

      return pos;
   }

   return filebuf::seekoff(off, way, mode);
}

ただし、100回のシークオフ試行ごとに位置を出力する診断では、毎回8192が生成されます。は?これはfilebuf自体のFILE *メンバーであるため、ファイル位置ポインターがfilebufによって行われるunderflow()呼び出しと同期していると思います。なぜ私は間違っているのですか?

アップデート

まず、私の投稿のこの後半部分は、移植性のないハッキングに関するものであることを理解していることを強調しておきます。それでも、ここで核心を理解していません。電話してみました

pos_type pos = _M_file.seekoff(0,ios::cur);

代わりに、これは8192でスタックするのではなく、サンプルファイルを順調に進みます。

最終更新

私の会社の内部では、パフォーマンスへの影響を十分に減らすためのいくつかの回避策を作成しました。

外部的には、DavidKraussがGNUのlibstdc++ストリームに対してバグを報告し、最近、PaoloCarliniが修正をチェックインしました。コンセンサスは、望ましくない動作は標準の範囲内であるが、私が説明したエッジケースには合理的な修正があったというものでした。

StackOverflow、David Krauss、Paolo Carlini、そしてすべてのGNU開発者に感謝します!

4

2 に答える 2

1

の要件はseekoff確かに紛らわしいですが、seekoff(0, ios::cur)何も同期しない特殊なケースであると想定されています。したがって、これはおそらくバグと見なされる可能性があります。

そしてそれはGCC4.2.1と4.5でもまだ起こります…</p>

問題は、(0, ios::cur)で特別な場合がないことです。_M_seekこれseekoffは、呼び出しfseekを使用して戻り値を取得するために使用されます。それが成功する限り、_M_seek無条件_M_set_buffer(-1);にを呼び出します。これにより、内部バッファが無効になります。次の読み取り操作により、が発生しunderflowます。

差分が見つかりました!変更を参照してください-473,41 +486,26。コメントは

    (seekoff): Simplify, set _M_reading, _M_writing to false, call
    _M_set_buffer(-1) ('uncommitted').

したがって、これはバグを修正するために行われたのではありません。

提出されたバグ: http://gcc.gnu.org/bugzilla/show_bug.cgi?id = 45628

于 2010-09-10T04:55:57.297 に答える
1

変更の正確な理由はわかりませんが、変更が行われたようです(GCC 3.4シリーズの変更ログを参照)。

  • 合理化されたstreambuf、filebuf、C標準I /Ostreambufと​​個別に同期。
  • ラージファイルサポート(32ビットシステムで2 GBを超えるファイル)。

ラージファイルサポートは、このような変更が必要になる大きな機能だと思います。IOStreamsは、ファイル全体をメモリにマップできるとは想定できなくなったためです。

との正しい同期cstdioは、ディスクへのフラッシュの回数を増やす必要がある操作でもあります。を使用して無効にすることができますstd::sync_with_stdio

于 2010-09-09T16:03:27.007 に答える