如果可以估计的话,TList、TObjectList 和普通数组之间的性能差异有多明显?
*总结:
请查看 Delphi 专家的专业评论。特别是对于我来说,我会尝试按照 David 的建议使用旧的 TList/TObjectList,并按照 A.Bouchez 的建议使用硬转换和 TObjectList.List 属性。以后重构的时候我会尝试TDynArray。
=================================================== ===================
假设我有一个 TAtom
类,如以下代码中所定义。运行时大约有数百
到数千
TAtom 实例,目前存储在动态数组
中。在运行时,我需要对所有现有 TAtom 实例的 TAtom.X/Y/Z
进行简单的浮点数学运算,每秒超过 30
次。
现在,我需要添加在运行时添加
、插入
、删除
TAtom 实例的功能。看来我的选择是(1)请求一个大数组; (2) 坚持动态数组并手动SetLength; (3)切换到常规TList; (4)切换到常规TObjectList。
除非有必要,否则我想避免 (1),因为这样我就必须更改很多函数签名。 (2) 看起来也不太好,因为 TList/TObjectList 似乎就是为这个任务而生的。然而,由于使用常规 TList/TObjectList 需要进行类型转换,有人可以评论一下可能的性能影响吗?我的意思是,如果在重写代码之前能够估计性能负担,那就最好了。如果性能明显下降,我可以使用其他技术吗?
此外,我想知道使用 TList 和 TObjectList 是否有性能差异?
TAtom = class
public
ElementZ: Integer;
X, Y, Z: Extended;
other variables: other types;
end;
TAAtom = array of TAtom;
*Summarization:
Please check the knowledgeable comments from the Delphi experts. Specifically for me, I would try to use old TList/TObjectList as David suggested, and use hard-cast and TObjectList.List property as A.Bouchez suggested. I will try TDynArray when refactoring in future.
=====================================================================
Say that I have a TAtom
class as defined in the following code. There are about hundreds
up to thousands
of TAtom instances at run time, stored in a dynamic array
for now. At run time, I need to do simple float math on TAtom.X/Y/Z
of all the existing TAtom instances more than 30
times per second.
Now, I need to add the ability of adding
, inserting
, deleting
of TAtom instances at run time. It seems that my choices are (1) request a big array; (2) stick to dynamic array and manually SetLength; (3) switch to regular TList; (4) switch to regular TObjectList.
I want to avoid (1) unless it is necessary, because I then have to change quite a lot function signatures. (2) looks not good either, because TList/TObjectList seems born for this task. However, because type-casting is needed using the regular TList/TObjectList, could some one comment on the possible performance hit? I mean, it would be best if the performance burden could be estimated before I rewrites the code. If the performance will drop noticeably, is there other technics that I could use?
Furthermore, I am wondering if there is performance difference between using TList and TObjectList?
TAtom = class
public
ElementZ: Integer;
X, Y, Z: Extended;
other variables: other types;
end;
TAAtom = array of TAtom;
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(8)
我可以在您的清单中添加另一个选择吗?
如果您不对
TAtom
中的数据使用任何继承功能,则可以使用record
而不是class
。每个类实例都需要在内存中分配,用零填充并单独初始化。Getmem/Freemem
总是有成本的,并且内存碎片会增加。预先分配的动态
记录数组
将比添加单个类实例更快。并且数据将更适合CPU L1/L2 缓存。对于插入和删除,如果您有大量项目,则此类记录的数组将比
TList
慢,因为将有更多数据需要删除/插入 (TList/TObjectList< /code> 都只维护一个指针列表)。为了更快地插入/删除,您最好使用链表。
由于内部通知,
TList/TObjectList
机制中存在一些开销。机制 并且GetItem()
属性可能比直接使用动态数组慢一些(因为范围检查)。但是使用我们的 TDynArray 包装器,您可以坚持使用动态数组,并且仍然有良好的性能、预分配功能和类似
TList
的方法。甚至还有更多可用方法,例如 SaveToStream、Slice、Reverse、使用外部索引排序等...适用于 Delphi 6 到 XE。
随着新版本的 Delphi 支持泛型,您应该更好地朝这个方向发展。
May I add another choice to your list?
If you don't use any inheritance feature for the data in
TAtom
, you could use arecord
instead of aclass
. Each class instance will require to be allocated in memory, filled with zero and initialized individually.Getmem/Freemem
always cost, and memory fragmentation will increase.A pre-allocated dynamic
array of record
will be faster than individual class instances for adding. And the data will fit better for CPU L1/L2 cache.For inserting and deleting, an array of such records will be slower than
TList
if you have a huge number of items, because there'll be more data to delete/insert (TList/TObjectList
both maintain just a list of pointers). For even faster insertion/deletion, you should better use a linked list.There is some overhead in the
TList/TObjectList
mechanism because of internal notification. mechanism And theGetItem()
property could be a bit slower (because of range checking) than using directly a dynamic array.But with our TDynArray wrapper, you could stick to a dynamic array, and still have good performance, pre-allocation features, and
TList
-like methods. And even more methods available, likeSaveToStream, Slice, Reverse
, sorting with external indexes and such...Works with Delphi 6 up to XE.
With newer version of Delphi supporting generics, you should better go into this direction.
如果您使用
Generics.Collections.TObjectList
并且不需要进行转换。对于您所描述的用法来说,性能应该很好。插入比添加到末尾要求更高,因为您需要将插入点之后的项目在列表中向上移动。
只要您避免使用
SetLength(A, Length(A)+1)
并选择更合理的分配策略,动态数组就等同于所有TList
之类的类。有时,当我尝试将大型列表维护为连续的内存块时,会遇到性能和内存碎片问题。然后我采取了二次分配方案。但是由于您的列表包含本质上是指针的对象引用,因此您已经有了隐式子分配。
这一切都有点推测性,你确实需要测量——否则我们只能猜测。
If you use
Generics.Collections.TObjectList<TAtom>
and there's no need for casting.Performance should be fine for the usage that you describe. Inserting is more demanding than adding to the end because you need to shift the items after the insertion point up the list.
So long as you avoid
SetLength(A, Length(A)+1)
and opt for a more sensible allocation strategy dynamic arrays are equivalent to all of theTList
like classes.On occasions I have had problems with performance and memory fragmentation when trying to maintain large lists as contiguous blocks of memory. Then I have resorted to a sub-allocation scheme. But since your lists contain object references which are essentially pointers, you already have implicit sub-allocation.
It's all somewhat speculative and you really need to measure – otherwise we can only guess.
如果您使用表单输入cast,
则会增加少量开销,这在您的情况下确实会增加。 如果您进行硬类型转换
但是,据我所知,
,实际上不会发生任何开销,因为类型转换是在没有检查的情况下执行的,而且我也相信它是在编译时完成的。至于你问题的另一方面,我认为它们已经全部得到了适当的涵盖......
If you type cast using the form
as small overhead will be added, which can really add up in your situation. However, if you hard typecast
as far as I know, no overhead actually occurs as the typecast is performed without checks and I also believe it is done at compile time.
As for the other aspect of your question, I think they have been all properly covered already...
制作一个测试项目,测量使用四种方法添加、插入和删除数千个 TAtom 实例的时间。然后决定使用哪一个。
在添加、插入和删除时,TList 和 TObjectList 可能比动态数组更快,因为动态数组需要不断地重新分配。 TList 和 TObjectList 的实现不会这样做。
Make a test project and measure the time of adding, inserting and deleting thousands of TAtom instances using the four methods. Then decide which one to use.
TList and TObjectList are probably faster than a dynamic array when adding, inserting and deleting, because the dynamic array constantly has to be reallocated. The implementation of TList and TObjectList doesn't do that.
由于 TObjectList 是 TList 的直接后代,因此性能将非常接近。
As TObjectList is a direct descendant of TList the performances will be very close.
第一个问题:我们是在谈论
Classes.TList
和Contnrs.TObjectList
还是分别在谈论Generics.Collections.TList
Generics .Collections.TObjectList
?如果我们谈论泛型,TList 和 TObjectList 都是使用动态数组实现的。如果直接使用动态数组或使用通用容器的更好接口之间存在任何性能差异,则可以忽略不计。
如果我们谈论“较旧的”
TList
和TObjectList
,那么我们只需将TList
与等效的动态数组进行比较,因为 < code>TObjectList 是TList
的后代,因此它继承了它的所有性能特征。TList
使用通过ReallocMem
分配的内存块。动态数组在内部执行相同的操作,因此不应该有显着差异!结论
如果两者之间存在任何性能差异,则可能是因为动态数组的天真使用使用了可怕的
SetLength(A, Length(A)+1)
,而在所有 Delphi 提供的容器中都有更好的实现以更大的块预分配内存。使用正确的代码,这些替代方案之间不应该有任何显着差异!First question: Are we talking about
Classes.TList
andContnrs.TObjectList
or are we talking aboutGenerics.Collections.TList
respectivelyGenerics.Collections.TObjectList
?If we're talking about generics, both TList and TObjectList are implemented using dynamic arrays. If there's any performance difference between directly using a dynamic array or using the nicer interface of the generic container, it's going to be negligible.
If we're talking about the "older"
TList
andTObjectList
, then we need to compare onlyTList
with the dynamic array equivalent, sinceTObjectList
is an descendant ofTList
so it inherits all it's performance characteristics.TList
uses a block of memory allocated usingReallocMem
. The dynamic array does the same thing internally, so there shouldn't be a significant difference!Conclusion
If there's any performance difference between the two, it's probably because naive use of dynamic array uses the dreaded
SetLength(A, Length(A)+1)
, while the better implementation in all Delphi-provided containers pre-allocate memory in larger chunks. With proper code there shouldn't be any significant difference between those alternative!TList
等完全执行了处理内存块或动态数组的代码必须执行的操作,但它们的实现已经针对常见情况进行了优化,并且包括有关内存管理器行为方式的考虑因素。一项标准可能是读取/更新与序列的比率。如果序列在创建后很少更新,那么使用动态数组应该可以感觉到更好的速度,因为使用
TList
等访问元素需要一个方法调用加上边界检查,如果使用的话还需要进行类型检查as
运算符。最后,在
TAtom
上完成的算术成本应该主导运行时间,从而使 dynarray 或TListXXX
的选择变得无关紧要。TList
etc. do exactly what code working on chunks of memory or dynarrays would have to do, but their implementation is already optimized for the common case, and includes considerations about how the memory manager behaves.One criteria could be the ratio of reads/updates to the sequence. If the sequence is updated infrequently after created, then there should be perceivable better speed with dynarrays, because access to elements with
TList
and the likes requires one method call plus bounds checking, plus a type check if you use theas
operator.In the end, the cost of arithmetic done on
TAtom
should dominate the run time, making the choice of dynarray orTListXXX
irrelevant.动态记录数组在访问项目时的影响最小,如果你的原子是对象,那么所有列表在访问速度方面都会有些相同。
但是,如果您执行其中许多操作,则关键问题将是插入和删除,所有列表和数组在这方面的性能都会很差,但这正是分析会告诉您的。
如果是这种情况,您可能需要考虑:
A dynamic arrays of records will have the lowest impact when accessing the items, if your atoms are objects, then all the lists are going to be somewhat equivalent in terms of access speed.
However, if you perform many of them, your key issue will be the inserts and deletes, for which all lists and arrays will perform poorly, but that is what profiling will tell you.
If that's the case, you might then want to consider: