我是德尔福程序员。 在程序中,我必须生成具有不同“分支”长度的二维数组。 它们非常大,操作需要几秒钟(烦人)。
例如:
var a: array of array of Word;
i: Integer;
begin
SetLength(a, 5000000);
for i := 0 to 4999999 do
SetLength(a[i], Diff_Values);
end;
我知道命令SetLength(a,dim1,dim2)但不适用。甚至没有为dim2设置最小值(> 0)并且从那里继续,因为dim2的min为0(一些“branches”可以为空)。
那么,有没有办法让它快速?不只是5..10%,而是非常快......
谢谢。
答案 0 :(得分:5)
在处理大量数据时,必须完成大量工作,这样就可以将理论上的最小值放在可以完成的时间内。
对于每次500万次迭代,您需要:
第1步完全由您控制,可以进行优化。但是,如果您使用现代版本的Delphi,它们的速度和它们的速度差不多。 (如果您使用的是旧版本,则可以从安装FastMM和FastCode中受益,这可以加快这些操作。)
如果合适,您可能会做的另一件事是延迟初始化。不要试图一次分配所有500万个数组,而是先做SetLength(a, 5000000);
。然后当你需要进入“分支”时,首先检查它的长度是否为0.如果是,它还没有被初始化,所以将它初始化为适当的长度。这不会节省整体时间,实际上它总共会花费更长的时间,但它确实分散了初始化时间,因此用户没有注意到。
如果你的初始化速度已经达到了它的速度,那么你的情况就是懒惰的初始化不能在这里使用,那么你基本上没有运气了。这是处理大量数据的代价。
答案 1 :(得分:2)
我刚刚使用Diff_Values
的常量测试了您的确切代码,并使用GetTickCount()
为基本时间计时。如果Diff_Values
为186
则需要1466毫秒,如果Diff_Values
为187
,则Out of Memory
失败。您知道,Out of Memory
表示Out of Address Space
,而不是Out of Memory
。
在我看来,你分配了大量数据,你用完了RAM而Windows开始分页,这就是为什么它很慢。在我的系统上,我有足够的RAM来为进程分配尽可能多的内容;确实如此,直到它失败。
答案 2 :(得分:2)
除了梅森的观点之外,还有一些需要考虑的想法:
如果分支长度在分配后永远不会改变,并且您在所有分支中存储在数组中的项目总数上限,那么您可以通过分配一个巨大的分区来节省一些时间记忆的大块并且自己在那个大块中分裂“分支”。您的数组将成为一维指针数组,该数组中的每个条目都指向该分支的数据的开头。使用单个指针变量跟踪大块中已用空间的“结束”,当需要为新的“分支”保留空间时,将当前的“结束”指针值作为新的开始分支并将“结束”指针增加分支所需的空间量。不要忘记围绕dword边界以避免错位处罚。
这种技术需要更多地使用指针,但是它提供了消除所有堆分配开销的潜力,或者至少用专门构建的非常简单,非常快速的子分配器替换通用堆分配,该子分配符合您的特定用途图案。它的执行速度应该更快,但是需要更多的时间来编写和测试。
此技术还可以避免堆碎片,并减少将所有内存释放到单个释放(而不是在当前模型中的数百万个单独分配)。
要考虑的另一个提示:如果你总是对每个新分配的数组“分支”做的第一件事就是将数据分配到每个槽中,那么你可以在Mason的例子中消除第3步 - 你不需要将其归零记忆如果您要做的就是立即将真实数据分配到其中。这将使你的内存写入操作减少一半。
答案 3 :(得分:1)
假设您可以将整个数据结构放入一个连续的内存块中,您可以一次性完成分配,然后接管索引。
注意:即使您无法将数据放入单个连续的内存块中,您仍然可以通过分配多个大块然后将它们拼接在一起来使用此技术。 / em>的
首先形成一个辅助数组colIndex
,它包含每行第一列的索引。将colIndex
的长度设置为RowCount+1
。您可以通过设置colIndex[0] := 0
然后colIndex[i+1] := colIndex[i] + ColCount[i]
来构建它。在for循环中执行此操作,该循环运行至RowCount
并包括colIndex[RowCount]
。因此,在最后一个条目colIndex[RowCount]
中,存储元素总数。
现在将a的长度设置为 function GetItem(row, col: Integer): Word;
begin
Result := a[colIndex[row]+col];
end;
。这可能需要一段时间,但它会比你以前做的更快。
现在你需要编写几个索引器。把它们放在一个班级或记录中。
getter看起来像这样:
row
安装者很明显。您可以内联这些访问方法以提高性能。将它们作为索引属性公开,以方便对象的客户端。
您需要添加一些代码来检查col
和colIndex
的有效性。您需要使用{$IFOPT R+}
作为后者。如果要模拟原始索引的范围检查,可以使用{{1}}使此检查可选。
当然,如果您想在初始实例化后更改任何列数,这是一个完全非首发!