没有人有解决速度问题的解决方案。但是让我们来看一下各种可以稍微加快速度的答案(尽管远远不足以认为问题已经解决)。
我们的测试将添加250个项目。
测试1 - 添加到原始TStrings - 每秒120,000个项目
首先,我们将向一个TStringList添加250个项目。在没有任何其他开销或成本的情况下,我们将看到它如何发生。
var
i: Integer;
sl: TStrings;
begin
sl := TStringList.Create;
sl.BeginUpdate;
for i := 1 to 250 do
sl.Items.Add('Test');
sl.EndUpdate;
end;
这在我的3.5 GHz i7-2700上以0.0081毫秒的速度添加了250个项目(每秒120,000个项目)。
这是我们能合理期望的最快速度。
测试2 - 向TComboBox.Items TStrings添加项目 - 每秒8个项目(比原来慢99.993%)
现在,我们的测试函数将实际向TComboBox.Items中添加内容。
var
i: Integer;
begin
for i := 1 to 250 do
AComboBox.Items.Add('Test');
end;
这将在124.9毫秒内添加250个项目(每秒8.00个项目)。
测试3 - 关闭重绘 - 每秒24个项目(慢99.98%)
接下来,我们通过调用WM_SETREDRAW
来关闭控件的绘制。
var
i: Integer;
begin
AComboBox.Perform(WM_SETREDRAW, 0, 0);
for i := 1 to 250 do
AComboBox.Items.Add('Test');
AComboBox.Perform(WM_SETREDRAW, 1, 0);
end;
这将在41.3397毫秒内添加250个项目(每秒24.2个项目)。
测试4 - Items.BeginUpdate - 每秒24个项目(比原速度慢99.98%)
您应该注意到,TCustomComboBoxStrings类(这是TComboBox内部使用的TStrings派生类)在您调用BeginUpdate / EndUpdate时已经为您调用了WM_SETREDRAW
:
procedure TCustomComboBoxStrings.SetUpdateState(Updating: Boolean);
begin
SendMessage(ComboBox.Handle, WM_SETREDRAW, Ord(not Updating), 0);
if not Updating then ComboBox.Refresh;
end;
这意味着你可以省去对
WM_SETREDRAW
的调用,直接使用经典的 Delphi 模式。
var
i: Integer;
begin
AComboBox.Items.BeginUpdate;
for i := 1 to 250 do
AComboBox.Items.Add('Test');
AComboBox.Items.EndUpdate;
end;
这样添加250个项目需要42.1127毫秒(每秒23.74个项目)。调用TStrings.BeginUpdate包装器的额外开销可以忽略不计。
测试5 - CB_INITSTORAGE - 每秒24个项目(慢99.98%)
然后我们来到神秘的CB_INITSTORAGE
方法。意图是告诉ComboBox你将要添加多少个项目,以及预计将占用多少内存(以字节为单位)。这使得ComboBox能够预先分配内存。
var
i: Integer;
begin
AComboBox.Perform(CB_INITSTORAGE, 250, 250*6*2);
AComboBox.Items.BeginUpdate;
for i := 1 to 250 do
AComboBox.Items.Add('Test');
AComboBox.Items.EndUpdate;
end;
这意味着在41.8486毫秒内添加了250个项目(每秒23.9个项目)。
这意味着它没有任何改进。事实上,我无法让CB_INITSTORAGE
在任何地方提供任何改进。也许在2004年的Windows XP中,COMBOBOX控件受益于预分配内存。但现在看来,该控件已经在内部解决了重复递增内存分配的问题,并且现在以块的形式分配内存,因此CB_INITSTORAGE
的用处已不复存在。
总结
测试 |
添加250个项目所需时间(毫秒) |
速度(项目/秒) |
添加到ComboBox |
124.9 毫秒 |
8 |
设置重绘 |
41.3397 毫秒 |
24.2 |
开始更新 |
42.1127 毫秒 |
23.8 |
初始化存储 |
41.8486 毫秒 |
23.9 |
原始TStringList |
0.0081 毫秒 |
120000 |
![enter image description here](https://istack.dev59.com/p2qz9.webp)
使用上述描述的方法,您可以提升性能:
- 在之前:比应有速度慢99.9933%
- 在之后:比应有速度慢99.9809%
这将带来0.01%的显著加速!
额外话题:
有些人可能会建议调用`.AddStrings`(或根据您的情况调用`.AddObjects`):
begin
AComboBox.Items.AddStrings(SourceList);
end;
他们没有意识到的是,AddStrings与上述的Case 4是完全相同的:
procedure TStrings.AddStrings(Strings: TStrings);
var
I: Integer;
begin
BeginUpdate;
try
for I := 0 to Strings.Count - 1 do
AddObject(Strings[I], Strings.Objects[I]);
finally
EndUpdate;
end;
end;
所以我们不再谈论AddStrings(或AddObjects),因为它们与我们的讨论无关。
额外阅读
TComboBox
需要80毫秒,这简直荒谬。所以,大家都可以回答被提出的问题。这是一个有效的问题,适用于比这个具体场景更广泛的受众。没有理由TComboBox
不能在小于10毫秒内添加大约100,000个项目。不要担心用户体验 - 这不是这个问题的一部分。回答这个问题就好。 - Ian Boyd