3

非常に大きなバイナリ ファイルがあり、入力ファイル内の ID に基づいて別のファイルを作成する必要があります。146 個の出力ファイルがあり、cstdlibandfopenとを使用していfwriteます。FOPEN_MAXは 20 なので、146 個の出力ファイルすべてを同時に開いておくことはできません。また、出力ファイルを開いたり閉じたりする回数を最小限に抑えたいと考えています。

出力ファイルに効果的に書き込むにはどうすればよいですか?

cstdlibレガシ コードのため、ライブラリも使用する必要があります。

実行可能ファイルは、UNIX と Windows のクロスプラットフォーム互換性も必要です。

4

11 に答える 11

5

あなたが取るかもしれないいくつかの可能なアプローチ:

  • FOPEN_MAX 未満の開いている出力ファイル ハンドルのキャッシュを保持します。既に開いているファイルに書き込みが必要な場合は、書き込みを行います。それ以外の場合は、キャッシュ内のハンドルの 1 つを閉じて、出力ファイルを開きます。入力ファイル内で特定のファイル セットのデータがグループ化されているという点で、データが一般的にまとめられている場合、これはファイル ハンドル キャッシュの LRU ポリシーで適切に機能するはずです。

  • ライブラリに任せるのではなく、自分で出力バッファリングを処理してください: 146 個の (または必要な数の) 出力バッファの独自のセットを保持し、それらに出力をバッファリングし、特定の出力が発生したときに open/flush/close を実行します。バッファがいっぱいになります。これを上記のアプローチと組み合わせて、開閉操作を最小限に抑えることもできます。

出力バッファがいっぱいになったとき、またはほぼいっぱいになったときに発生する可能性のあるエッジ条件を十分にテストしてください。

于 2010-06-16T16:07:18.067 に答える
3

また、入力ファイルをスキャンして、各出力 ID のリストを作成し、最初にすべての file1 エントリを書き込み、次にすべての file2 エントリなどを書き込むようにソートすることも価値がある場合があります。

于 2010-06-16T15:55:04.657 に答える
1

どうにかして FOPEN_MAX の最大値を増やすことができない場合は、要求の単純なキューを作成し、必要に応じてファイルを閉じて再度開くことができます。

また、各ファイルの最後の書き込み時間を追跡し、最後に書き込まれたファイルを開いたままにすることもできます。

于 2010-06-16T15:49:11.703 に答える
0

最も安全な方法は、ファイルを開いて書き込み後にフラッシュし、最近の書き込みが行われなくなったら閉じることです。プログラムの制御外にある多くのことが原因で、ファイルの内容が破損する可能性があります。このことを念頭に置いて読み進めてください。

std::mapまたはポインターstd::vectorを保持することをお勧めします。FILEを使用mapすると、ID によってファイル ポインタにアクセスできます。ID 範囲が小さい場合は、vector要素を予約し、ID をインデックスとして使用して を作成できます。これにより、同時に多くのファイルを開いたままにすることができます。データ破損の概念に注意してください。

同時に開くファイルの制限は、オペレーティング システムによって設定されます。たとえば、OS に最大 10 のファイルがある場合、11 番目のファイルが要求されたときに手配する必要があります。

もう 1 つのトリックは、各ファイルの動的メモリにバッファーを確保することです。すべてのデータが処理されたら、ファイル (または複数) を開き、(1 つを使用してfwrite) バッファーを書き込み、閉じて次に進みます。ファイルではなくデータ処理中にメモリに書き込むため、これはより高速になる場合があります。興味深い点として、OS もバッファをハード ドライブにページングする場合があります。バッファーのサイズと量は、プラットフォームに依存する最適化の問題です (適切な組み合わせを得るには、調整してテストする必要があります)。OS がメモリをディスクにページングすると、プログラムの速度が低下します。

于 2010-06-16T17:40:08.100 に答える
0

「ファイルを開く回数が最も少ない」戦略:

ファイルのオープンとクローズの最小回数を達成するには、入力を複数回読み取る必要があります。毎回、並べ替えが必要な ID のサブセットを選択し、それらのレコードのみを出力ファイルに抽出します。

各スレッドの疑似コード:

  1. ファイルを実行し、すべての一意の ID を収集します。
  2. fseek()入力の先頭に戻ります。
  3. 19 個の ID のグループごとに:
    1. IDごとにファイルを開きます。
    2. 入力ファイルを実行し、一致するレコードを対応する出力ファイルに追加します。
    3. この 19 個の出力ファイルのグループを閉じます。
    4. fseek()入力の先頭に。

この方法は、最終的にスレッドがファイルのまったく異なる部分を読み取ることになるため、複数のスレッドではうまく機能しません。その場合、ファイル キャッシュを効率的に使用することは困難です。バリアを使用して、スレッドを多かれ少なかれロックステップに保つことができます。

「最小限のファイル操作」戦略

複数のスレッドと大きなバッファー プールを使用して、入力の実行を 1 回だけ行うことができます。これは、より多くのファイルのオープンとクローズを犠​​牲にして行われます (おそらく)。ファイル全体がソートされるまで、各スレッドは次のようになります。

  1. 入力の次の未読ページを選択します。
  2. その入力を、出力ファイルごとに 1 つのバッファーで、2 ページのバッファーに並べ替えます。1 つのバッファ ページがいっぱいになると、次のようになります。
    1. ページを使用不可としてマークします。
    2. このページの page-counter 値が最も小さい場合は、 を使用してファイルに追加しますfwrite()。そうでない場合は、最低になるまで待ちます (うまくいけば、これはあまり発生しません)。
    3. ページを使用可能としてマークし、次のページ番号を付けます。

出力ファイルをディスクにフラッシュする単位を変更できます。おそらく、出力ファイルごとに一度に 200 ページを収集するのに十分な RAM があるでしょうか?

注意事項:

  • データはページ配置されていますか? そうでない場合は、「次のページ」を賢く読む必要があります。
  • fwrite()2 つのスレッドが同時に同じ出力ファイルにアクセスしていないことを確認してください。その場合、いずれかのページが破損する可能性があります。
于 2010-06-16T18:25:43.780 に答える
0

これらの出力に「リアルタイム」で書き込むことが重要かどうか、または書き込まれるデータの量については言及していません。制約によっては、すべての出力をバッファリングして、ソフトウェアの実行の最後に書き込むという選択肢もあります。

これの変形は、固定サイズの内部バッファーをセットアップすることです。内部バッファーの制限に達したら、ファイルを開き、追加して閉じ、さらに出力するためにバッファーをクリアします。バッファーは、オープン/クローズ サイクルの数を減らし、通常はファイル システムが適切に処理するように設定されている書き込みのバーストを提供します。これは、ある程度リアルタイムの書き込みが必要な場合や、データが利用可能なメモリよりも大きく、ファイル ハンドルがシステムの最大値を超えている場合に当てはまります。

于 2010-06-16T16:02:33.493 に答える
0

OPにリストされた制約を使用してそれを書いていた場合、146個のバッファを作成してそれらにデータを挿入し、最後にバッファを順番に見ていき、単一のファイルハンドルを閉じたり開いたりします。

あなたはコメントで、速度が大きな懸念事項であり、単純なアプローチは遅すぎると述べました。

検討を開始できることがいくつかあります。1 つは、バイナリ ファイルを連続したストリップに再編成することです。これにより、並列操作が可能になります。もう 1 つは、ファイルハンドル コレクションに対して最も最近使用されていない方法です。別のアプローチとして、8 つの異なるプロセスに分岐し、それぞれが 19 ~ 20 個のファイルに出力することもできます。

これらのアプローチのいくつかは、バイナリ構成 (高度に断片化されているか高度に順次的か) に応じて、多かれ少なかれ実用的に記述できます。

主な制約は、バイナリ データのサイズです。キャッシュよりも大きいですか?メモリより大きい?テープデッキからストリーミング?継続的にセンサー ストリームから出てきて、メモリ内に「ファイル」としてのみ存在しますか? それらのそれぞれは、異なる最適化戦略を提示します...

もう 1 つの問題は、使用パターンです。ファイルへの書き込みがときどき急増していませんか? それとも大量のチャンクが数回しか書き込まれていませんか? これにより、ファイルハンドルのさまざまなキャッシング/ページング戦略の有効性が決まります。

于 2010-06-16T18:19:54.223 に答える
0

*nix システムを使用していると仮定すると、制限はプロセスごとであり、システム全体ではありません。つまり、複数のプロセスを起動できることを意味し、それぞれがフィルタリングしている ID のサブセットを担当します。それぞれがそのプロセスの FOPEN_MAX 内に保つことができます。

1 つの親プロセスが入力ファイルを読み取ってから、パイプ特殊ファイルを介してさまざまな「書き込み」プロセスにデータを送信することができます。

于 2010-06-16T18:23:26.467 に答える
0

2ステップでできます。

1) 最初の 19 個の ID を 1 つのファイルに書き込み、次の 19 個の ID を次のファイルに書き込みます。したがって、このステップでは、8 つの出力ファイル (および入力ファイル) を並行して開く必要があります。

2)そのように作成されたファイルごとに、19個(最後のファイルは13個のみ)の新しいファイルを作成し、IDを書き込みます。

入力ファイルの大きさやそれに含まれる ID データセットの数に関係なく、常に 163 個のファイルを開いたり閉じたりする必要があります。ただし、データを 2 回書き込む必要があるため、id-datasets が非常に小さく、ランダムに分散している場合にのみ価値があります。

ほとんどの場合、ファイルを頻繁に開いたり閉じたりする方が効率的だと思います。

于 2010-06-16T16:38:19.087 に答える
0

解決策は明らかです - N 個のファイルを開きます。ここで、N は FOPEN_MAX よりもいくらか小さくなります。次に、入力ファイル全体を読み取り、最初の N 個の出力ファイルの内容を抽出します。次に、出力ファイルを閉じ、入力を巻き戻し、繰り返します。

于 2010-06-16T15:53:58.853 に答える
0

まず、できるだけ並行して実行していただければ幸いです。同時に複数のファイルに書き込めない理由はありません。thomaskが言ったことを実行してリクエストをキューに入れることをお勧めします。その後、いくつかのスレッド同期を使用して、キュー全体がフラッシュされるまで待機してから、次のラウンドの書き込みを許可することができます。

于 2010-06-16T15:54:42.100 に答える