1

さて、私はVirtualStringTreeを使用して一種のプロセスマネージャーを作成しています...

1000msに設定されたタイマーでツリーを更新したために問題が発生しました(CPU使用率が高すぎて、アプリケーションが大量のデータ(約20列を埋める)を取得できません。

それで、ある種のキャッシュシステムを構築して、何かが変更されたときにのみツリーを更新できるようにするにはどうすればよいのでしょうか。これが、アプリケーションのCPU使用率を大幅に低下させる鍵であると思われます。

をちょきちょきと切る:

type
  TProcessNodeType = (ntParent, ntDummy);

  PProcessData = ^TProcessData;

  TProcessData = record

   pProcessName : String;
   pProcessID,
   pPrivMemory,
   pWorkingSet,
   pPeakWorkingSet,
   pVirtualSize,
   pPeakVirtualSize,
   pPageFileUsage,
   pPeakPageFileUsage,
   pPageFaults : Cardinal;
   pCpuUsageStr: string;
   pIOTotal: Cardinal;
...

  end;

アプリケーションが起動したら、実行中のすべてのプロセスでツリーを埋めます。これは一度だけ呼び出されることを覚えておいてください。後でアプリケーションを実行すると、新しいプロセスまたはwmiを介して終了するプロセスの通知を受け取ったので、後でタイマーで次のプロシージャを呼び出してツリーを更新する必要はありません...

procedure FillTree;
begin
var
  NodeData: PProcessData;
  Node: PVirtualNode;
  ParentNode: PVirtualNode;
  ChildNode: PVirtualNode;
  Process: TProcessItem;
  I : Integer;
begin
   ProcessTree.BeginUpdate;
   for I := 0 to FRunningProcesses.Count - 1 do
  begin
    Process := FRunningProcesses[i];

    NodeData^.pProcessID := ProcessItem.ProcessID;
    NodeData^.pProcessName := ProcessItem.ProcessName;

...

必要なすべてのデータを取得して、次のようにツリーに保存するクラスがあります。

var
  FRunningProcesses: TProcessRunningProcesses;

したがって、実行中のすべてのプロセスを列挙したい場合は、次のように呼び出します。

  // clears all data inside the class and refills everything with the new data... 
  FRunningProcesses.UpdateProcesses;

問題は、変更されたデータだけでなく、すべてを列挙しているときにここから始まります。これは、CPUを大量に消費します。

procedure TMainForm.UpdateTimerTimer(Sender: TObject);
var
  NodeData: PProcessData;
  Node : PVirtualNode;
  Process: TProcessItem;
  I: Integer;
begin
   for I := 0 to FRunningProcesses.Count - 1 do
   begin
      Application.ProcessMessages;

      Process := FRunningProcesses[I];

      // returns PVirtualNode if the node is found inside the tree
      Node := FindNodeByPID(Process.ProcessID);

      if not(assigned(Node)) then
      exit;

      NodeData := ProcessVst.GetNodeData(Node);

      if not(assigned(NodeData)) then
       exit;

     // now starting updating the tree 
     // NodeData^.pWorkingsSet := Process.WorkingsSet; 
....

基本的に、タイマーはCPU使用率と、次のようなプロセスから取得できるすべてのメモリ情報にのみ必要です。

  • Priv.Memory
  • ワーキングセット
  • ピークワーキングセット
  • 仮想サイズ
  • PageFileの使用法
  • PageFileのピーク使用量
  • ページフォールト
  • CPU使用率
  • スレッド数
  • ハンドル数
  • GDIハンドル数
  • ユーザーハンドル数
  • 合計CPU時間
  • ユーザーCPU時間
  • カーネルCPU時間

したがって、上記のデータをキャッシュして比較する必要があると思います。データが変更された場合、またはどのように、そして何が最も効率的か疑問に思っているだけではないでしょうか。

4

2 に答える 2

0

現在表示されているノードのデータのみを更新する必要があります。vst.getfirstvisible vst.getnextvisibleこれらのノードを反復処理するために使用できます。

2番目の方法も簡単です。レコードの代わりにオブジェクトを使用します。オブジェクト使用法のサンプルコード

さまざまな値にゲッターを使用します。それらのゲッターは、プロセスに値を照会します。多分あなたはここに制限が必要です。1秒ごとにのみデータを更新します。

これで、vstを毎秒無効なステータスに設定するだけで済みます。

vst.invalidate

これにより、vstは表示領域を再描画する必要がありました。

ただし、これはすべて、データが変化する値で並べ替えられていない場合にのみ機能します。これが必要な場合は、すべてのレコードを更新する必要があり、これがボトルネックだと思います。COMとWMIは純粋なAPIよりもはるかに遅いことを忘れないでください。(遅い)ループを避け、プロファイラーを使用して遅い部分を見つけます。

于 2011-07-07T10:00:28.037 に答える
0

VT のノード データが TProcessItem を直接指すようにすることをお勧めします。

プロの:

  1. を取り除きFindNodeByPIDます。からすべてのアイテムを更新してから FRunningProcesses呼び出すだけVT.Refreshです。処理が終了したら、該当する項目を から削除しFRunningProcessesます。FindNodeByPID現在、すべての VT ノードをループし、それらのデータを取得して PID をチェックする非常にコストのかかる検索があります。
  2. Process := FRunningProcesses[I]TProcessData レコード全体の不要なデータ コピーがある場所を取り除きます (ところで、これはとにかく実行する必要がありますが、代わりにポインターを使用してください)。
  3. // now starting updating the treeブロック全体を取り除きます。
  4. 一般に、この変更により余分なエンティティが減少し、アプリケーションの更新とデバッグに非常に適しています。

短所:

  1. VT と FRunningProcesses を同期させておく必要があります。しかし、それはごく些細なことです。
于 2014-11-05T15:31:26.810 に答える