0

我有一个可能需要处理数十亿个对象的应用程序。每个对象都是 TRange 类类型。这些范围是在取决于某些条件和其他对象属性的算法的不同部分创建的。因此,如果您有 100 个项目,则无法直接创建第 100 个对象而不创建所有先前的对象。如果我创建所有(数十亿个)对象并添加到集合中,系统将抛出 Outofmemory 错误。现在我想遍历每个对象主要有两个目的:

  1. 为每个 TRange 对象应用一个操作(例如:输出某些属性)
  2. 获得某个属性的累积总和。(例如:每个范围都有一个权重属性,我想检索总权重,它是所有范围权重的总和)。

如何在不引发内存不足的情况下有效地为这些对象创建迭代器?

我通过将函数指针传递给算法函数来处理第一种情况。例如:

procedure createRanges(aProc: TRangeProc);//aProc is a pointer to function that takes a    //TRange
var range: TRange;
  rangerec: TRangeRec;
begin
  range:=TRange.Create;
  try 
    while canCreateRange do begin//certain conditions needed to create a range
      rangerec := ReturnRangeRec;
      range.Update(rangerec);//don't create new, use the same object.
      if Assigned(aProc) then aProc(range);
    end;
  finally
    range.Free;
  end;
end;

但是这种方法的问题是,要添加一个新功能,比如检索我之前提到的总重量,我必须复制算法函数或传递一个可选的输出参数。请提出一些想法。

提前谢谢
大家

4

6 回答 6

8

对于如此大量的数据,您只需要在内存中保存一部分数据。其他数据应序列化到硬盘驱动器。我解决了这样一个问题:

  1. 我创建了一个扩展存储,可以将自定义记录存储在内存中或硬盘上。此存储具有最大数量的记录,可以同时存在于内存中。
  2. 然后我从自定义记录类中派生出记录类。这些类知道如何从硬盘驱动器存储和加载自己(我使用流)。
  3. 每次您需要新的或已经存在的记录时,您都会向扩展存储请求此类记录。如果超过最大对象数,存储会将一些最少使用的记录流回硬盘驱动器。

这样记录是透明的。您总是像在内存中一样访问它们,但它们可能首先从硬盘驱动器加载。它真的很好用。顺便说一句,RAM 的工作方式非常相似,因此它只保存硬盘驱动器上所有数据的某个子集。这是你的工作集。

我没有发布任何代码,因为它超出了问题本身的范围,只会混淆。

于 2010-10-11T11:32:45.520 回答
1

看看 TgsStream64。这个类可以通过文件映射处理海量数据。

http://code.google.com/p/gedemin/source/browse/trunk/Gedemin/Common/gsMMFStream.pas

于 2010-10-11T11:56:08.463 回答
1

但是这种方法的问题是,要添加一个新功能,比如检索我之前提到的总重量,我必须复制算法函数或传递一个可选的输出参数。

通常是这样完成的:您编写一个枚举器函数(就像您所做的那样),它接收一个回调函数指针(您也这样做)和一个无类型指针(“数据:指针”)。您定义一个回调函数以使第一个参数是相同的无类型指针:

TRangeProc = procedure(Data: pointer; range: TRange);

procedure enumRanges(aProc: TRangeProc; Data: pointer);
begin
  {for each range}
    aProc(range, Data);
end;

然后,如果你想对所有范围求和,你可以这样做:

TSumRecord = record
  Sum: int64;
end;
PSumRecord = ^TSumRecord;

procedure SumProc(SumRecord: PSumRecord; range: TRange);
begin
  SumRecord.Sum := SumRecord.Sum + range.Value;
end;

function SumRanges(): int64;
var SumRec: TSumRecord;
begin
  SumRec.Sum := 0;
  enumRanges(TRangeProc(SumProc), @SumRec);
  Result := SumRec.Sum;
end;

无论如何,如果你需要创造数十亿的任何东西,你可能做错了(除非你是科学家,对极其大规模和详细的东西进行建模)。如果您每次想要其中之一时都需要创建数十亿个东西,那就更是如此。这从来都不是好事。尝试考虑替代解决方案。

于 2010-10-11T12:28:44.573 回答
0

这部分:

因此,如果您有 100 个项目,则无法直接创建第 100 个对象而不创建所有先前的对象。

听起来有点像计算斐波那契。也许您可以重用一些 TRange 对象而不是创建冗余副本?是描述这种方法的 C++ 文章 - 它通过将已计算的中间结果存储在哈希图中来工作。

于 2010-10-11T12:36:12.163 回答
0

处理数十亿个对象是可能的,但您应该尽可能避免它。仅当您绝对必须这样做时才这样做......
我确实创建了一个需要能够处理大量数据的系统。为此,我使我的对象“可流化”,以便我可以将它们读/写到磁盘。它周围的一个更大的类用于决定何时将对象保存到磁盘并因此从内存中删除。基本上,当我调用一个对象时,这个类会检查它是否被加载。如果不是,它将再次从磁盘重新创建对象,将其放在堆栈顶部,然后将底部对象从该堆栈移动/写入磁盘。结果,我的堆栈具有固定(最大)大小。它允许我使用无限数量的对象,并且性能也相当不错。
不幸的是,我没有该代码可用了。大约 7 年前,我为以前的雇主写了这篇文章。我确实知道您需要为流支持编写一些代码,并为维护所有这些对象的堆栈控制器编写更多代码。但从技术上讲,它允许您创建无限数量的对象,因为您正在用 RAM 内存换取磁盘空间。

于 2010-10-11T14:00:07.363 回答
0

“Runner”有一个很好的答案如何处理这个!

但我想知道你是否可以做一个快速修复:制作更小的 TRange 对象。也许你有一个大祖先?你能看一下 TRange 对象的实例大小吗?也许你最好使用打包记录?

于 2010-10-11T12:25:56.743 回答