0

何十億ものオブジェクトを処理する必要があるアプリケーションがあります。各オブジェクトは TRange クラス タイプです。これらの範囲は、特定の条件やその他のオブジェクト プロパティに依存するアルゴリズムのさまざまな部分で作成されます。その結果、100 個のアイテムがある場合、それ以前のオブジェクトをすべて作成しないと、100 番目のオブジェクトを直接作成することはできません。すべての (数十億の) オブジェクトを作成してコレクションに追加すると、システムは Outofmemory エラーをスローします。ここで、主に 2 つの目的で各オブジェクトを反復処理します。

  1. TRange オブジェクトごとに操作を適用するには (例: 特定のプロパティを出力する)
  2. 特定のプロパティの累積合計を取得するには (例: 各範囲には重みプロパティがあり、すべての範囲の重みの合計である totalweight を取得したい)。

Outofmemory を発生させずにこれらのオブジェクトの Iterator を効果的に作成するにはどうすればよいですか?

関数ポインターをアルゴリズム関数に渡すことで、最初のケースを処理しました。例:

procedure createRanges(aProc: TRangeProc);//aProc is a pointer to function that takes a    //TRange
var range: TRange;
  rangerec: TRangeRec;
begin
  range:=TRange.Create;
  try 
    while canCreateRange do begin//certain conditions needed to create a range
      rangerec := ReturnRangeRec;
      range.Update(rangerec);//don't create new, use the same object.
      if Assigned(aProc) then aProc(range);
    end;
  finally
    range.Free;
  end;
end;

しかし、このアプローチの問題は、新しい機能を追加することです。たとえば、前述の合計重量を取得するには、アルゴリズム関数を複製するか、オプションの出力パラメーターを渡す必要があります。いくつかのアイデアを提案してください。

よろしくお願いし
ます

4

6 に答える 6

8

このような大量のデータの場合、メモリ内のデータの一部のみが必要です。他のデータはハードドライブにシリアル化する必要があります。私はこのような問題に取り組みました:

  1. カスタムレコードをメモリまたはハードドライブに保存できる拡張ストレージを作成しました。このストレージには、メモリ内で同時に存続できる最大数のレコードがあります。
  2. 次に、カスタムレコードクラスからレコードクラスを派生させました。これらのクラスは、ハードドライブから自分自身を保存およびロードする方法を知っています(私はストリームを使用します)。
  3. 新しいレコードまたは既存のレコードが必要になるたびに、そのようなレコードを拡張ストレージに要求します。オブジェクトの最大数を超えると、ストレージは最も使用されていないレコードの一部をハードドライブにストリーミングします。

このようにして、レコードは透過的になります。それらは常にメモリ内にあるかのようにアクセスしますが、最初にハードドライブからロードされる場合があります。それは本当にうまくいきます。ちなみに、RAMは非常によく似た方法で動作するため、ハードドライブ上のすべてのデータの特定のサブセットのみを保持します。これがワーキングセットです。

質問自体の範囲を超えており、混乱するだけなので、コードは投稿しませんでした。

于 2010-10-11T11:32:45.520 に答える
1

TgsStream64 を見てください。このクラスは、ファイル マッピングによって大量のデータを処理できます。

http://code.google.com/p/gedemin/source/browse/trunk/Gedemin/Common/gsMMFStream.pas

于 2010-10-11T11:56:08.463 に答える
1

しかし、このアプローチの問題は、新しい機能を追加することです。たとえば、前述の合計重量を取得するには、アルゴリズム関数を複製するか、オプションの出力パラメーターを渡す必要があります。

通常は次のように行われます: コールバック関数ポインター (あなたもそうしました) と型指定されていないポインター ("Data: pointer") を受け取る列挙関数を (あなたがしたように) 書きます。最初のパラメーターが同じ型指定されていないポインターになるように、コールバック関数を定義します。

TRangeProc = procedure(Data: pointer; range: TRange);

procedure enumRanges(aProc: TRangeProc; Data: pointer);
begin
  {for each range}
    aProc(range, Data);
end;

次に、たとえば、すべての範囲を合計したい場合は、次のようにします。

TSumRecord = record
  Sum: int64;
end;
PSumRecord = ^TSumRecord;

procedure SumProc(SumRecord: PSumRecord; range: TRange);
begin
  SumRecord.Sum := SumRecord.Sum + range.Value;
end;

function SumRanges(): int64;
var SumRec: TSumRecord;
begin
  SumRec.Sum := 0;
  enumRanges(TRangeProc(SumProc), @SumRec);
  Result := SumRec.Sum;
end;

とにかく、何十億ものものを作成する必要がある場合は、おそらく間違っています (科学者で、非常に大規模で詳細なものをモデル化している場合を除きます)。必要なたびに何十億ものものを作成する必要がある場合はなおさらです。これは決して良いことではありません。別の解決策を考えてみてください。

于 2010-10-11T12:28:44.573 に答える
0

何十億ものオブジェクトを扱うことは可能ですが、できるだけ避けるべきです。これは、絶対に必要な場合にのみ行ってください...
大量のデータを処理できるようにする必要があるシステムを一度作成しました。そのために、オブジェクトを「ストリーミング可能」にして、ディスクに読み書きできるようにしました。オブジェクトをいつディスクに保存してメモリから削除するかを決定するために、その周りのより大きなクラスが使用されました。基本的に、オブジェクトを呼び出すと、このクラスはそれがロードされているかどうかをチェックします。そうでない場合は、ディスクからオブジェクトを再作成し、それをスタックの一番上に置き、一番下のオブジェクトをこのスタックからディスクに移動/書き込みます。その結果、スタックの (最大) サイズは固定されていました。また、オブジェクトを無制限に使用でき、パフォーマンスも適度に良好でした。
残念ながら、そのコードはもう入手できません。私は約7年前に前の雇用主のためにそれを書きました。ストリーミングをサポートするためのコードと、これらすべてのオブジェクトを維持するスタック コントローラーのためのコードをさらに記述する必要があることはわかっています。ただし、RAM メモリをディスク容量と交換しているため、技術的には無制限の数のオブジェクトを作成できます。

于 2010-10-11T14:00:07.363 に答える
0

「ランナー」はこれをどのように処理するかについて良い答えを持っています!

しかし、簡単な修正ができるかどうかを知りたいのですが、より小さな TRange オブジェクトを作成してください。ひょっとして、ご先祖様が多いのでは?TRange オブジェクトのインスタンス サイズを見ていただけますか。たぶん、パックされたレコードを使用した方がよいでしょうか?

于 2010-10-11T12:25:56.743 に答える
0

この部分:

その結果、100 個のアイテムがある場合、それ以前のオブジェクトをすべて作成しないと、100 番目のオブジェクトを直接作成することはできません。

フィボナッチの計算に少し似ています。冗長コピーを作成する代わりに、TRange オブジェクトの一部を再利用できますか? これは、このアプローチを説明する C++ の記事です。これは、既に計算された中間結果をハッシュ マップに格納することによって機能します。

于 2010-10-11T12:36:12.163 に答える