VirtualStringTree使用缓存系统更新数据

发布于 2024-11-19 03:38:36 字数 2552 浏览 5 评论 0原文

好吧,我正在使用 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
  • 工作集
  • 峰值工作集
  • 虚拟大小
  • 页面文件使用
  • 情况 峰值页面文件使用情况
  • 页面错误
  • Cpu 使用情况
  • 线程计数
  • 句柄计数
  • GDI 句柄计数
  • 用户句柄计数
  • 总 Cpu 时间
  • 用户 Cpu 时间
  • 内核 Cpu时间

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

Well, I'm using VirtualStringTree to create kind of a process manager...

I run into trouble because of updating the tree with a timer set to 1000ms (cpu usage is too high for my application retrieving a lot of data (filling about 20 columns).

So I wonder how would one build kind of a cache system so I can update the tree only when something changed which I guess seems to be the key decrementing the cpu usage for my application a lot?

Snip:

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;

If my application starts I fill the tree with all running processes.
Remember this is called only once, later when the application runs I got notified of new processes or processes which are terminated via wmi so I dont need to call the following procedure in the timer later to update the tree...

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;

...

I have a Class which will retrieve all the data I want and store it into the tree like:

var
  FRunningProcesses: TProcessRunningProcesses;

So if I want to enumerate all running processes I just give it a call like:

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

The problem starts here while I enumerate everything and not only data which had changed which is quite cpu intensive:

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; 
....

Basically the timer is only needed for cpu usage and all memory informations I can retrieve from a process like:

  • Priv.Memory
  • Working Set
  • Peak Working Set
  • Virtual Size
  • PageFile Usage
  • Peak PageFile Usage
  • Page Faults
  • Cpu Usage
  • Thread Count
  • Handle Count
  • GDI Handle Count
  • User Handle Count
  • Total Cpu Time
  • User Cpu Time
  • Kernel Cpu Time

So I think the above data must be cached and compared somehow if its changed or not just wonder how and what will be most efficient?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

凉宸 2024-11-26 03:38:36

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

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

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

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

vst.invalidate

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

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

You need only update the data in nodes which are currently are visible.
you can use vst.getfirstvisible vst.getnextvisible to iterate thru these nodes.

the second way is also easy.
use objects instead of the record. sample code of object usage

use getters for the different values.
those getters query the processes for the values.
maybe you need here a limit. refresh data only every second.

now you only need to set the vst into an invalidated status every second.

vst.invalidate

this forced the vst to repaint the visible area.

but all this works only if your data is not sorted by any changing values.
if this necessary you need to update all record and this is your bottle neck - i think.
remember COM and WMI are much slower than pure API.
avoid (slow) loops and use a profiler to find the slow parts.

蘸点软妹酱 2024-11-26 03:38:36

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

优点:

  1. 摆脱 FindNodeByPID。只需更新所有项目
    FRunningProcesses,然后调用VT.Refresh。当过程是
    终止,从 FRunningProcesses 中删除相应的项目。
    目前,您在 FindNodeByPID 中进行的搜索非常昂贵,其中
    您循环遍历所有 VT 节点,检索它们的数据并检查
    PID。
  2. 删除您拥有的 Process := FRunningProcesses[I]
    整个 TProcessData 记录的不必要的数据副本(顺便说一句,
    无论如何都应该这样做,请改用指针)。
  3. 摆脱整个 // 现在开始更新树 块。
  4. 一般来说,通过此更改,您可以减少多余的实体,这对于应用程序更新和调试非常有好处。

缺点:

  1. 您必须保持 VT 和 FRunningProcesses 同步。但这是微不足道的。

I'd recommend you to have your VT's node data point directly to TProcessItem.

Pro's:

  1. Get rid of FindNodeByPID. Just update all the items from
    FRunningProcesses and then call VT.Refresh. When the process is
    terminated, delete corresponding item from FRunningProcesses.
    Currently you have quite expensive search in FindNodeByPID where
    you loop through all VT nodes, retrieve their data and check for
    PID.
  2. Get rid of Process := FRunningProcesses[I] where you have
    unnecessary data copy of the whole TProcessData record (btw, that
    should be done anyway, use pointers instead).
  3. Get rid of the whole // now starting updating the tree block.
  4. In general, by this change you decrease excess entities what is very good for application updating and debugging.

Con's:

  1. You'll have to keep VT and FRunningProcesses in sync. But that's quite trivial.
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文