Author: Clever Components
Delphi 3,4,5 TCollection Performance Issues and Solutions
Answer:
If you are using TCollection classes in your Delphi 3,4 or 5 applications than you
will find this article quite interesting.
Firstly let us try fairly simple code:
1 procedure TForm1.Button1Click(Sender: TObject);
2 var
3 old: TCollection;
4 i: integer;
5 begin
6 old := TCollection.Create(TCollectionItem);
7 for i := 0 to 100000 do
8 begin
9 old.Add;
10 end;
11 Windows.beep(900, 1000); // hi-freq beep after we done with adding empty items
12 old.Free;
13 Windows.beep(100, 1000); // low-freq beep after we done with destroying empty
14 items
15 end;
You might think that low-freq beep will follow right after hi-freq beep (well what
can be faster that just simply destroy all collection items) - but IT IS not!
In fact it takes 10-20 seconds to destroy collection which hold few dozen thousands
items - and worse of all your CPU will be 100% busy. We bumped into this problem
when our clients complains that application is "freeze PC for a few minutes".
To understand why it is happening you need take closer look at
TCollectionItem.Destroy, TCollectionItem.SetCollection and TCollection.RemoveItem
functions which located at classes.pas - last one is the key to understanding this
problem.
You also might want to compare your TCollection.RemoveItem version to Delphi 6
TCollection.RemoveItem code:
16 { classes.pas from Delphi 6 }
17
18 procedure TCollection.RemoveItem(Item: TCollectionItem);
19 begin
20 Notify(Item, cnExtracting);
21 if Item = FItems.Last then
22 FItems.Delete(FItems.Count - 1) // that will fix original problem
23 else
24 FItems.Remove(Item);
25 Item.FCollection := nil;
26 NotifyDesigner(Self, Item, opRemove);
27 Changed;
28 end;
Now you probably will want to fix it. But seems it is not so easy because
TCollection.RemoveItem is not a virtual or dynamic function.
Here is two solutions:
You will need to alter classes.pas - put that TCollection.RemoveItem code from
Delphi 6 into your version of classes.pas.
Copy new (fixed) classes.pas into your project directory and put it at the first
position in your .dpr uses section like this:
29 program Project1;
30
31 uses
32 classes in 'classes.pas' // new classes.pas with fixed TCollection.RemoveItem
33 Forms,
34 Unit1 in 'Unit1.pas' {Form1};
35
36 {$R *.res}
37
38 begin
39 Application.Initialize;
40 Application.CreateForm(TForm1, Form1);
41 Application.Run;
42 end.
43
44 //Now your project will be compiled with new version of TCollection.
45
46 //In some situations it is not so convenient or even not possible to use altered
47 classes.pas and in this case we have other trick for you.
48
49 type
50 { TFixCollection - fix TCollection.RemoveItem issue in Delphi 3,4,5 }
51 TFixCollection = class(TCollection)
52 public
53 { Unfortunately Clear is not a virtual or dynamic procedure so we will
54 have to reintroduce it }
55 procedure Clear;
56 destructor Destroy; override;
57 end;
58
59 procedure TFixCollection.Clear;
60 var
61 i: integer;
62 AList, OrgList: TList;
63 begin
64 AList := TList.Create;
65 try
66 OrgList := TList(PDWORD(DWORD(Self) + $4 + SizeOf(TPersistent))^);
67
68 { Save original pointers to collection items }
69 for i := 0 to OrgList.Count - 1 do
70 AList.Add(OrgList[i]);
71
72 OrgList.Clear;
73
74 { Destroy collection items }
75 for i := 0 to AList.Count - 1 do
76 TCollectionItem(AList[i]).Free;
77 finally
78 AList.Free;
79 end;
80 inherited;
81 end;
82
83 destructor TFixCollection.Destroy;
84 begin
85 Clear;
86 inherited;
87 end;
88
89 { Let's try again ! }
90
91 procedure TForm1.Button2Click(Sender: TObject);
92 var
93 old: TFixCollection;
94 i: integer;
95 begin
96 old := TFixCollection.Create(TCollectionItem);
97 for i := 0 to 100000 do
98 old.Add;
99 Windows.beep(900, 1000); // hi-freq beep after we done with adding empty items
100 old.Free;
101 Windows.beep(100, 1000); // low-freq beep after we done with destroying empty
102 items
103 end;
As you can see now it works just fine. We used one trick which gives us access to
protected section of TCollection.
You can use both techniques in your applications written on Delphi versions 3,4,5.
For your convenience you can download Delphi 3,4,5 TCollection performance issue demo sources here CollectionPerformanceDemo.zip
|