2

Platform: Delphi with VirtualTreeView SVN 5.1.0 & OmniThreadLibrary 3 SVN & Delphi XE2

Originally I thought that the problem was VirtualTreeView. I need to add node to VST every 1s or less.But It seems soon or later the CPU rate will hit 50% or more ultil the whole application becomes completely non-responsive.

  var FAbort:Boolean;
  .....

  procedure TrmMain.btnAddNodeClick(Sender: TObject);
    begin
      while not FAbort do
      begin
        VstMain.RootNodeCount:= VstMain.RootNodeCount + 1;
        Sleep(10);
        Application.ProcessMessages;
      end;
    end;

Anyone can help? TIA!

EDIT: It seems the problem comes from the OTL. When use the code above, minimize the application the CPU alway less than 1%, even change 10ms sleep to 1ms.

But, the code below will reproduces the problem which trouble me.

procedure TForm1.btn5Click(Sender: TObject);
var
  I: Integer;
begin
  for I := 0 to 1 do
    CreateTask(
      procedure(const Task: IOmniTask)
      begin
        while not FAbort do
        begin
          Task.Comm.Send(1, 0);
          Sleep(10);
        end;
      end).OnMessage(
      procedure(const Task: IOmniTaskControl; const Msg: TOmniMessage)
      begin
        vst1.AddChild(nil);
      end).Run;
end;

PS: For avoid the flood to the OTL default 1000 queue size, I DO have a lock in each thread that wait for the add node completed before next Task.Comm.Send operation.

PPS: The 10ms here is just for quick reproduce the problem, not in real situation. So don't bother ask why?

OK,the conclusion is: simply not add too much nodes at single node if you need to update this node periodically, the more node the more cpu time to update them.

XARA
  • 23
  • 3
  • 2
    Could you add more badness? Endless While looping, with Sleep plus Application.ProcessMessages? It's a symphony of Bad. How about adding access violations and memory leaks? Seriously. What were you thinking? Add a TTimer, and add a node from the timer, not in the loop. GUI applications are not yours to control with endless loops. Learn to love event driven programming and to understand that Sleep(X) and Application.ProcessMessages are not the way to solve your issues. – Warren P Jan 10 '13 at 03:07
  • Be cool, I'm just try to reproduce the problem, the real code not like the one I put above. The add node event only happened when there is a new log comes. – XARA Jan 10 '13 at 03:29
  • Hook up a profiler and find out where it's spending its time. Remember that the internal data structure is a doubly linked list, so traversing it takes longer as the structure grows. – Rob Kennedy Jan 10 '13 at 04:48
  • 1
    It's pretty hard to understand the problem if you describe behaviour and then post fake code rather than the real code. – David Heffernan Jan 10 '13 at 07:31
  • why do you have to add a node every **10ms**? who is able to read every **10ms** a new entry? – Sir Rufo Jan 10 '13 at 08:03
  • You can't say you've isolated your issue if you can reproduce it with only the above evil code, but the above evil code is not your real problem. Use a real profiler (AQTime) and you will know,instead of playing games. – Warren P Jan 10 '13 at 13:56
  • I don't understand the relation of minimizing the application with threading. You think threading is the problem because when minimized the app behaves differently? – Sertac Akyuz Jan 10 '13 at 18:38
  • Your question title is horrible, since you've discovered this has nothing to do with VirtualTreeView. I don't see how your non-question can be turned into a real question. How about deleting it? I edited your title to remove virtualtreeview reference since it doesn't really apply, but your question is still asking us to debug your giant pile of invisible code, and so I am voting to close. – Warren P Jan 10 '13 at 19:21
  • @Warren P: Thanks for your help. I code a app need to be run in the backgroud 24hr, the frequency of add node isn't so extreme than the demo which I uploaded to RapidShare. But, after hours and hours running, eventually the application will be freezed. – XARA Jan 11 '13 at 00:55
  • @Sertac Akyuz: There IS differece after minimize the application. For better a better feel download my "EVIL CODE" I've upload to rapidshare. :D – XARA Jan 11 '13 at 01:00
  • @XARA - I didn't say there's no difference. I asked why should that difference be caused by OTL. – Sertac Akyuz Jan 11 '13 at 01:44
  • @Sertac Akyuz: It's just for a better view of cpu rate raising by avoid the UI to update itself. – XARA Jan 11 '13 at 03:42
  • @XARA - Ah! I misunderstood that part in the question. Thanks for clearing up. – Sertac Akyuz Jan 11 '13 at 03:54

2 Answers2

2

In my view, you should not synchronously update a view when the underlying model changes, especially not Each and Every time.

The VirtualTreeView is a visual control. Human beings do not need to see the tree update in real time, any more than 3x per second is wasted. So don't do it.

Instead, update your model (objects, classes), set a notification flag, and then (from a TTimer), do an asynchronous SINGLE update of VirtualStringTree.RootNodeCount, that occurs 3x per second maximum. (More than one setting of this update flag each 333 msec leads to a minimum waiting period of 333 msec until it actually updates.) That's my arbitrary user-interface "faster than this and it's just flicker and churn, and no use whatsoever" constant.

Delphi's own developers ran into this with VirtualTreeView, I know because I logged the QC bug involved. If you did enough "OutputDebugString" messages in Delphi 2009, the IDE would become non-responsive. Why? Because they did what you're doing. Don't do it. I'm not saying that a user click should cause a 333 ms wait before the screen refreshes. I'm saying that some process that is generating tree content continuously should only notify the tree's "view controller" of changes 3 times per second, maximum.

Warren P
  • 65,725
  • 40
  • 181
  • 316
  • P: Thank you, I got your point. The Delphi IDE's Log view uses VST indeed, and DO have that problem. Andreas Hausladen, the author of IDE Fix Pack, added a timer which is 70ms 0r 80ms interval to update the VST and fixed that problem. If the child nodes of a single node (in my case, RootNode) grow too fast and too much ,then the overhead of update UI each time is too expensive. In my code, I simple add a limit which is 10,000, if the child nodes is over it, save it before clear them all, at last, this solved my problem.As you mentioned, periodicity add a bunch of them is much more efficiently. – XARA Jan 12 '13 at 00:22
  • Sorry, I didn't get the @ to work. when I type your name, it just eat it. – XARA Jan 12 '13 at 00:44
  • If this solved your problem then you should edit your title and question to make this easier to find for the future people. – Warren P Jan 12 '13 at 20:17
1

If you add nodes with the AddChild() function, things might peform better than accessing the RootNodeCount property.

Eg something like:

procedure TMyForm.OnTimer( Sender: TObject );
var
  Node: PVirtualNode;
begin
  Node := MyTree.AddChild( nil );
  // fill in details with GetNodeData( Node );

end;

Better yet: use a timer and try to add a few items per time interval:

procedure TMyForm.OnTimer( Sender: TObject );
begin
  AddToList( ... );
end;

procedure TMyForm.OnTimer( Sender: TObject );
var
  Node: PVirtualNode;
  Item: <Some iterator>;
begin
  MyTree.BeginUpdate();
  try
    for Item in <somelist> do begin
      Node := MyTree.AddChild( nil );
      // fill in details with GetNodeData( Node );
    end;
  finally
    MyTree.EndUpdate();
  end; 
end;
Ritsaert Hornstra
  • 5,013
  • 1
  • 33
  • 51
  • I agree that expanding `RootNodeCount` 100 times a second at values of RootNodeCount > 1000 might grow increasingly expensive. There may be some insane rebuild-entire-tree happening. But the only way to know is for our clever original post asking person to run in AQTime and Find Out for Himself. – Warren P Jan 10 '13 at 13:59