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:
- Get rid of
FindNodeByPID
. Just update all the items fromFRunningProcesses
and then callVT.Refresh
. When the process is terminated, delete corresponding item fromFRunningProcesses
. Currently you have quite expensive search inFindNodeByPID
where you loop through all VT nodes, retrieve their data and check for PID. - 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). - Get rid of the whole
// now starting updating the tree
block. - In general, by this change you decrease excess entities what is very good for application updating and debugging.
Con's:
- You'll have to keep VT and FRunningProcesses in sync. But that's quite trivial.
精彩评论