如何在没有“Outofmemory”错误的情况下处理数十亿个对象

时间:2010-10-11 11:08:47

标签: delphi design-patterns oop ooad

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

  1. 为每个TRange对象应用操作(例如:输出某些属性)
  2. 获取某个属性的累积总和。(例如:每个范围都有一个权重属性,我想要检索总权重,即所有范围权重的总和)。
  3. 如何在不提高Outofmemory的情况下为这些对象有效地创建迭代器?

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

    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;
    

    但是这种方法的问题在于,要添加一个新功能,比如检索前面提到的Total权重,要么我必须复制算法函数,要么传递一个可选的out参数。请提出一些想法。

    提前谢谢大家 普拉迪普

6 个答案:

答案 0 :(得分:8)

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

  1. I创建了一个扩展存储,可以将自定义记录存储在内存或硬盘上。此存储具有可以在内存中同时存在的最大记录数。
  2. 然后我从自定义记录类中派生出记录类。这些类知道如何从硬盘驱动器存储和加载自己(我使用流)。
  3. 每当您需要新的或已存在的记录时,您都会向扩展存储区请求此类记录。如果超过最大对象数,则存储会将一些最少使用的记录流回硬盘驱动器。
  4. 这样记录是透明的。您总是像访问内存一样访问它们,但它们可能首先从硬盘驱动器加载。它工作得很好。顺便说一句,RAM以非常类似的方式工作,因此它只保存硬盘上所有数据的某个子集。这是你的工作集。

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

答案 1 :(得分:1)

看看TgsStream64。该类可以通过文件映射处理大量数据。

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

答案 2 :(得分:1)

  

但是这种方法的问题在于,要添加一个新功能,比如检索前面提到的Total权重,要么我必须复制算法函数,要么传递一个可选的out参数。

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

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;

无论如何,如果你需要创造数十亿的任何东西你可能做错了(除非你是一个科学家,建模一些非常大规模和详细的东西)。如果你需要在每次需要其中之一时创建数十亿的东西,那就更是如此了。这永远不会好。试着考虑替代解决方案。

答案 3 :(得分:0)

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

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

答案 4 :(得分:0)

这部分:

  

结果,如果你有100件物品,   你无法直接创造第100个   对象没有创建所有先前的   对象。

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

答案 5 :(得分:0)

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