1

好吧,我正在使用 VirtualStringTree 来创建一种流程管理器......

由于使用设置为 1000 毫秒的计时器更新树,我遇到了麻烦(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 使用和我可以从以下进程中检索的所有内存信息:

  • 私有内存
  • 工作集
  • 峰值工作集
  • 虚拟尺寸
  • 页面文件使用
  • 峰值页面文件使用
  • 页面错误
  • CPU使用率
  • 线程数
  • 处理计数
  • GDI 句柄计数
  • 用户句柄计数
  • 总 CPU 时间
  • 用户 CPU 时间
  • 内核CPU时间

所以我认为上面的数据必须被缓存并以某种方式进行比较,如果它改变了,或者不只是想知道如何以及什么是最有效的?

4

2 回答 2

0

您只需要更新当前可见的节点中的数据。您可以使用vst.getfirstvisible vst.getnextvisible这些节点进行迭代。

第二种方法也很简单。使用对象而不是记录。对象使用示例代码

对不同的值使用吸气剂。那些 getter 查询进程的值。也许你需要一个限制。每秒刷新一次数据。

现在您只需要每秒将 vst 设置为无效状态。

vst.invalidate

这迫使 vst 重新绘制可见区域。

但是只有当您的数据没有按任何变化的值排序时,所有这些才有效。如果有必要,您需要更新所有记录,这是您的瓶颈 - 我认为。记住 COM 和 WMI 比纯 API 慢得多。避免(慢)循环并使用分析器查找慢部分。

于 2011-07-07T10:00:28.037 回答
0

我建议您将 VT 的节点数据直接指向 TProcessItem。

专业人士:

  1. 摆脱FindNodeByPID. 只需更新所有项目 FRunningProcesses,然后调用VT.Refresh. 当进程终止时,从 中删除相应的项目FRunningProcessesFindNodeByPID目前,您在遍历所有 VT 节点、检索它们的数据并检查 PID的地方进行了非常昂贵的搜索。
  2. 摆脱Process := FRunningProcesses[I]整个 TProcessData 记录的不必要数据副本的位置(顺便说一句,无论如何都应该这样做,而是使用指针)。
  3. 摆脱整个// now starting updating the tree街区。
  4. 通常,通过这种更改,您可以减少多余的实体,这对于应用程序更新和调试非常有用。

缺点:

  1. 您必须保持 VT 和 FRunningProcesses 同步。但这很微不足道。
于 2014-11-05T15:31:26.810 回答