6

私はシミュレーションプログラムに取り組んでいます。

プログラムが最初に行うことの1つは、巨大なファイル(28 mb、約79,000行)を読み取り、各行(約150フィールド)を解析し、オブジェクトのクラスを作成して、TStringListに追加することです。

また、実行中にオブジェクトを追加する別のファイルを読み込みます。最終的には、約85,000個のオブジェクトになります。

私はDelphi2007を使用していて、プログラムは大量のメモリを使用していましたが、問題なく動作しました。Delphi XEにアップグレードし、プログラムを移行したところ、より多くのメモリを使用しているため、実行の途中でメモリが不足してしまいます。

したがって、Delphi 2007では、最初のファイルを読み込んだ後、1​​.4ギガを使用することになります。これは明らかに膨大な量ですが、XEでは、ほぼ1.8ギガを使用することになります。これは非常に大きく、不足して取得することになります。エラー

だから私の質問は

  1. なぜそんなに多くのメモリを使用しているのですか?
  2. XEで2007よりもはるかに多くのメモリを使用しているのはなぜですか?
  3. これについて私は何ができますか?ファイルの大きさや長さを変更することはできません。行ごとにオブジェクトを作成して、どこかに保存する必要があります。

ありがとう

4

10 に答える 10

10

メモリを節約できるアイデアが 1 つだけあります。

データを元のファイルに残したままにして、インメモリ構造からそれらを指すだけにすることができます。

たとえば、大きなログ ファイルをほぼ瞬時に参照するために行うことです。ログ ファイルの内容をメモリ マップし、それをすばやく解析してメモリ内に有用な情報のインデックスを作成し、内容を動的に読み取ります。読み取り中に文字列は作成されません。必要なインデックスを含む動的配列を使用して、各行の先頭へのポインターのみ。呼び出しTStringList.LoadFromFileは明らかに遅くなり、メモリを消費します。

コードはここにあります-TSynLogFileクラスを参照してください。秘訣は、ファイルを 1 回だけ読み取り、その場ですべてのインデックスを作成することです。

たとえば、UTF-8 ファイルのコンテンツからテキスト行を取得する方法は次のとおりです。

function TMemoryMapText.GetString(aIndex: integer): string;
begin
  if (self=nil) or (cardinal(aIndex)>=cardinal(fCount)) then
    result := '' else
    result := UTF8DecodeToString(fLines[aIndex],GetLineSize(fLines[aIndex],fMapEnd));
end;

JSON コンテンツを解析するために、まったく同じトリックを使用します。このような混合アプローチの使用は、最速の XML アクセス ライブラリで使用されます。

高レベルのデータを処理し、それらを高速にクエリするには、レコードの動的配列と、最適化されたラッパー (同じユニット内) を使用してみてTDynArrayくださいTDynArrayHashed。レコードの配列はメモリの消費が少なくなり、データが断片化されないため検索が高速になり (順序付けられたインデックスまたはハッシュを使用するとさらに高速になります)、コンテンツへの高レベルのアクセスが可能になります。 (たとえば、カスタム関数を定義して、メモリ マップ ファイルからデータを取得できます)。動的配列はアイテムの高速削除には適合しません (または、ルックアップ テーブルを使用する必要があります) - しかし、多くのデータを削除していないと書いているので、問題にはなりません。

そのため、構造が重複することはなくなり、ロジックは RAM 内にあり、データはメモリ マップト ファイル上にあります。同じロジックが複数のソース データ ファイルに完全にマップされる可能性があるため、ここに「s」を追加しました (必要な場合)。いくつかの「マージ」と「ライブリフレッシュ」AFAIK)。

于 2011-08-25T17:47:11.740 に答える
6

コードとクラス宣言を見ずにファイルをオブジェクトに解析すると、28MBのファイルが1.4GB相当のオブジェクトに拡張される理由はわかりません。また、またはのTStringList代わりに保存していると言います。これは、ある種の文字列->オブジェクトのキー/値のマッピングとして使用しているように聞こえます。もしそうなら、あなたはXEのユニットのクラスを見たいかもしれません。TListTObjecListTDictionaryGenerics.Collections

XEでより多くのメモリを使用している理由はstring、Delphi 2009でタイプがANSI文字列からUTF-16文字列に変更されたためです。Unicodeが必要ない場合は、TDictionaryを使用してスペースを節約できます。

また、さらに多くのメモリを節約するために、79,000個のオブジェクトすべてをすぐに必要としない場合に使用できる別のトリックがあります。それは遅延読み込みです。アイデアは次のようになります。

  • ファイルをTStringListに読み込みます。(これにより、ファイルサイズとほぼ同じメモリが使用されます。Unicode文字列に変換される場合は2倍になる可能性があります。)データオブジェクトを作成しないでください。
  • 特定のデータオブジェクトが必要な場合は、文字列リストをチェックしてそのオブジェクトの文字列キーを検索するルーチンを呼び出します。
  • その文字列にオブジェクトが関連付けられているかどうかを確認します。そうでない場合は、文字列からオブジェクトを作成し、それをTStringListの文字列に関連付けます。
  • 文字列に関連付けられているオブジェクトを返します。

これにより、メモリ使用量とロード時間の両方を抑えることができますが、ロード直後にすべて(または大部分)のオブジェクトが必要ない場合にのみ役立ちます。

于 2011-08-25T16:35:17.530 に答える
3

コメントを読むと、データを Delphi からデータベースに移動する必要があるように思えます。

そこから、臓器提供者と受容者を簡単にマッチングできます*)。

SELECT pw.* FROM patients_waiting pw
INNER JOIN organs_available oa ON (pw.bloodtype = oa.bloodtype) 
                              AND (pw.tissuetype = oa.tissuetype)
                              AND (pw.organ_needed = oa.organ_offered)
WHERE oa.id = '15484'

新しい臓器提供者 15484 と一致する可能性のある患者を見たい場合。

メモリでは、一致する少数の患者のみを処理します。

*)すべての認識を超えて単純化されていますが、それでも.

于 2011-08-25T17:45:07.403 に答える
3
  • Delphi 2007(およびそれ以前)では、文字列は Ansi 文字列です。つまり、すべての文字が 1 バイトのメモリを占有します。

  • Delphi 2009(およびそれ以降)では、文字列は Unicode 文字列です。つまり、すべての文字が 2 バイトのメモリを占有します。

私の知る限り、Delphi 2009+TStringListオブジェクトに Ansi 文字列を使用させる方法はありません。の機能を実際に使用していますTStringListか? そうでない場合は、代わりに文字列の配列を使用できます。

その後、当然、次のいずれかを選択できます

type
  TAnsiStringArray = array of AnsiString;
  // or
  TUnicodeStringArray = array of string; // In Delphi 2009+, 
                                         // string = UnicodeString
于 2011-08-25T16:08:08.137 に答える
1

Andreas の投稿に加えて:

Delphi 2009 より前では、文字列ヘッダーは 8 バイトを占めていました。Delphi 2009 以降、文字列ヘッダーは 12 バイトになります。そのため、すべての一意の文字列は以前よりも 4 バイト多く使用し、+ 各文字が 2 倍のメモリを必要とします。

また、Delphi 2010 から、TObject は 4 バイトではなく 8 バイトを使用し始めたと思います。したがって、Delphi によって作成された単一のオブジェクトごとに、Delphi はさらに 4 バイトを使用するようになりました。これらの 4 バイトは、私が信じている TMonitor クラスをサポートするために追加されました。

どうしてもメモリを節約する必要がある場合、繰り返される文字列値がたくさんある場合に役立つちょっとしたトリックを次に示します。

var
  uUniqueStrings : TStringList;

function ReduceStringMemory(const S : String) : string;
var idx : Integer;
begin
  if not uUniqueStrings.Find(S, idx) then
    idx := uUniqueStrings.Add(S);

  Result := uUniqueStrings[idx]
end;

これは、繰り返される文字列値が多数ある場合にのみ役立つことに注意してください。たとえば、このコードは私のシステムで 150MB 少なく使用します。

var sl : TStringList;
  I: Integer;
begin
  sl := TStringList.Create;
  try
    for I := 0 to 5000000 do
      sl.Add(ReduceStringMemory(StringOfChar('A',5)));every
  finally
    sl.Free;
  end;
end;
于 2011-08-25T17:03:34.553 に答える
1

また、大きなファイルの場合、数 GB に達する可能性のあるプログラムで多くの文字列を読み取ります。

64 ビット XE2 を待たずに、次の 1 つのアイデアを参考にしてください。

個々の文字列を文字列リストに保存すると、メモリの点で遅く、無駄になることがわかりました。糸を一緒にブロックしてしまいました。私の入力ファイルには、5 行から 100 行までの論理レコードがあります。したがって、stringlist に各行を格納する代わりに、各レコードを格納します。必要な行を見つけるためにレコードを処理しても、処理にほとんど時間がかからないので、これは可能です。

論理レコードがない場合は、ブロッキング サイズを選択し、(たとえば) 10 個または 100 個の文字列を 1 つの文字列として (区切り文字で区切って) 格納することもできます。

もう 1 つの方法は、それらを高速で効率的なディスク上のファイルに格納することです。私がお勧めするのは、Arnaud Bouchezによるオープン ソースのSynopse Big Tableです。

于 2011-08-25T17:31:10.280 に答える
0

AnsiString で構成されているという点で、Delphi 2007 の TStringList に似ています。

それでも、他の人が述べたように、XE は Delphi 2007 よりも多くのメモリを使用します。

巨大なフラット ファイルの全文を文字列リストにロードする価値が本当にわかりません。他の人は、Arnaud Bouchez のアプローチや SqLite の使用などのビッグテーブル アプローチを提案しており、私はそれらに同意します。

ファイル全体をメモリにロードする単純なクラスを作成し、行ごとのオブジェクト リンクをメモリ内の巨大な ansichar バッファに追加する方法を提供することもできると思います。

于 2011-08-26T04:14:51.710 に答える
0

Delphi 2009 から、文字列だけでなくすべての TObject のサイズが 2 倍になりました。( Delphi 2009 で TObject のサイズが 2 倍になった理由を参照してください)。しかし、オブジェクトが 85,000 個しかない場合、この増加は説明できません。これらのオブジェクトに多くのネストされたオブジェクトが含まれている場合にのみ、それらのサイズがメモリ使用量の一部になる可能性があります。

于 2011-08-26T05:49:02.000 に答える
0

リストに重複する文字列がたくさんありますか? 一意の文字列のみを保存しようとすると、メモリ サイズの削減に役立つ場合があります。可能な(しかし単純すぎるかもしれない)答えについては、文字列プールに関する私の質問を参照してください 。

于 2011-08-26T06:56:38.393 に答える
0

メモリの断片化に悩まされていませんか?

必ず最新のFastMM (現在は 4.97) を使用してから、Delphi メモリの実際の使用状況を示すメモリ マップ フォームを含むUsageTrackerDemoデモをご覧ください。

最後に、プロセス メモリがどのように使用されているかを示すVMMapを見てください。

于 2011-08-26T08:20:33.403 に答える