开发者

VirtualStringTree updating data using cache system

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开发者_StackOverflow 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?


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.


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

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜