我正试图在我的Delphi应用程序中挤出所有性能,现在我开始使用一个与动态数组一起工作的程序。其中最慢的一行是
SetLength(结果,Len);
用于初始化动态数组。当我查看SetLength过程的代码时,我发现它远非最佳。呼叫顺序如下:
_DynArraySetLength - > DynArraySetLength
DynArraySetLength获取数组长度(初始化为零),然后使用ReallocMem,这对于启动也是不必要的。
我一直在做SetLength来初始化动态数组。也许我错过了什么?有更快的方法吗?
编辑:描述主算法需要占用大量空间,实际上是不必要的,因为它试图优化其中的一小部分。一般来说,它是车辆路径问题(http://en.wikipedia.org/wiki/Vehicle_routing_problem)的证明。我确实需要数以万计的分配,因为我必须保留所有数据,并将其分开保存。 Probalby如果我能想到一些聪明的数据结构会有所帮助,但我能想到的任何东西都会大大增加代码的复杂性。 基本上我已经在算法级别上做了我所能做的一切,所以现在我正试图从低级别的东西中获得我能做的一切。所以这是一个相当狭隘的问题:是否有可能增加这个特定的电话。我认为要做到这一点,我需要根据SetLength代码编写自己的初始化函数。并使其内联。答案 0 :(得分:10)
这是一个更多的评论,但由于在评论中发布大量代码并不漂亮,我将在此处发布。
有时,如果您不知道最终会有多少元素,那么编写这样的代码可能很诱人:
var
Arr: array of cardinal;
procedure AddElement(const NewElement: cardinal);
begin
SetLength(Arr, length(Arr) + 1);
Arr[high(Arr)] := NewElement;
end;
这非常糟糕,因为每次添加新元素时都需要重新分配内存。更好的方法(如果可能)是找到元素数量的上限,例如MAX_ELEMENTS = 100000;然后最初设置长度:
SetLength(Arr, MAX_ELEMENTS);
然后你创建一个像
这样的变量var
ActualNumberOfElements: cardinal = 0;
并写
procedure AddElement(const NewElement: cardinal);
begin
Arr[ActualNumberOfElements] := NewElement;
inc(ActualNumberOfElements);
end;
完成填充数组后,只需将其截断:
SetLength(Arr, ActualNumberOfElements);
答案 1 :(得分:3)
如果你只是打电话一次并且需要很长时间,那么你要求的RAM比现有的要多,导致操作系统换出其他东西以试图为你腾出空间。修复:添加更多RAM,使用更小的阵列。
如果你在一个循环中调用它,那么“realloc”就是问题所在。每次调用它时,逐渐增加数组的长度,Delphi重新分配整个数组,然后它将所有内容从旧数组复制到新数组。效率不高。
答案 2 :(得分:2)
“过早优化是万恶之源。”
我怀疑你不得不关心一生一次的初始化......
除非你称之为无数次,否则我建议你重新设计你的代码。
答案 3 :(得分:2)
Max写道:
我对一个函数有很多调用 哪一次SetLength(结果, Len)。
检查你如何使用你的功能:你真的需要对这个分配函数进行大量不同的调用吗?你可以像弗朗索瓦建议的那样重新设计代码以减少通话次数吗?
如果你真的需要对你的函数进行一次不同的调用,并且需要加快速度,我认为你需要保留动态数组并使用不同的结构。
但在此之前,并进入一个完整的调试地狱,我会强烈加入弗朗索瓦的建议,并尝试重新设计代码。
也许您可以告诉我们更多关于您的算法的信息?是关于处理图形3D结构吗?
[编辑]好的,如果它是关于解决NP完全问题,我会尽量避免分配。
大多数情况下,你可以给出数组/堆栈/结构大小的上限 - 例如:#cities,#vehicles + #drivers,#roads * #cities ......
对于那些部分,我建议分配一次最大可能的数组,并手动处理你只使用前n行等的事实。
考虑到之后可以节省的计算时间,即使在n ^ 2或n ^ 3中分配结构也是可以接受的。
优化SetLength:你的建议是有意义的。
然而,如果动态数组很适合编写Delphi代码 - 主要是因为它们的“自动构造函数”语义 - 它们是,恕我直言,不能很好地适应高性能计算 - 它严重依赖于RTTI,引用计数可以给你不时出现惊喜...
你的建议是动态数组语义的变化。试着看看“改变你的数据类型”的解决方案是否真的不可能写入&调试。
答案 4 :(得分:2)
而不是多次调用包含SetLength
的函数,而是预先分配存储,并将预分配的数组传递给函数。一种这样的方式是这样的:
const
NUM_ARRAYS = 1000;
ARRAY_SIZE = 1000;
type
TMyArray = array [0..ARRAY_SIZE] of Integer;
var
MyStorage: array[0..NUM_ARRAYS] of TMyArray;
procedure DoWork(var AArray: TMyArray);
begin
Blah;
end;
procedure MainLoop;
var
i: Integer;
begin
for i := 0 to High(MyStorage) do
begin
DoWork(MyStorage[i]);
end;
end;
这比在内部循环中调用SetLength要快 。需要注意的主要是为硬件使用太多内存。
答案 5 :(得分:1)
SetLength中最慢的部分不是“无法调用”或者其他东西 - 它是由内存管理器执行的内存分配(在你的情况下,ReallocMem将是简单的GetMem,因为源指针是零)。
那么,你使用什么内存管理器?在没有FastMM的情况下,你是否有机会参加D7?
尝试安装FastMM并查看它是否有所帮助。