在Delphi中初始化数组的更快方法

时间:2010-05-12 09:21:56

标签: performance delphi

我正试图在我的Delphi应用程序中挤出所有性能,现在我开始使用一个与动态数组一起工作的程序。其中最慢的一行是

SetLength(结果,Len);

用于初始化动态数组。当我查看SetLength过程的代码时,我发现它远非最佳。呼叫顺序如下:

_DynArraySetLength - > DynArraySetLength

DynArraySetLength获取数组长度(初始化为零),然后使用ReallocMem,这对于启动也是不必要的。

我一直在做SetLength来初始化动态数组。也许我错过了什么?有更快的方法吗?

编辑:描述主算法需要占用大量空间,实际上是不必要的,因为它试图优化其中的一小部分。一般来说,它是车辆路径问题(http://en.wikipedia.org/wiki/Vehicle_routing_problem)的证明。我确实需要数以万计的分配,因为我必须保留所有数据,并将其分开保存。 Probalby如果我能想到一些聪明的数据结构会有所帮助,但我能想到的任何东西都会大大增加代码的复杂性。 基本上我已经在算法级别上做了我所能做的一切,所以现在我正试图从低级别的东西中获得我能做的一切。所以这是一个相当狭隘的问题:是否有可能增加这个特定的电话。我认为要做到这一点,我需要根据SetLength代码编写自己的初始化函数。并使其内联。

6 个答案:

答案 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并查看它是否有所帮助。