循环遍历大型数据集时,内存中要保留多少数据

时间:2017-11-17 16:14:00

标签: c# .net algorithmic-trading

我正在尝试创建一个交易模拟器来测试很长一段时间的策略。

我正在使用1分钟的数据点。因此,如果我要进行10年的模拟,那将是大约3,740,000的价格(价格等级如下所示)。模拟可能比10年更长,但使用这个例子。

     class Price
     {
          DateTime DatePrice
          double Open
          double High
          double Low
          double Close
     }

我的模拟器工作但是我能帮助感觉我做这件事的方式并不是最优的。

所以目前我所做的就是从我的SQL数据库中获取价值多少374,400的价格。我之所以这样做是因为我不想使用太多的内存(这可能是错误的,不知道)。

现在循环时间代码也将使用之前的10个价格。因此,在凌晨2点30分,代码将从早上2点20分回顾价格,之前的所有价格现在都是多余的。因此,如果我在记忆中保持374,400的价格,那对我来说似乎有些浪费。

            time       Close
            00:00      102
            00:01      99
            00:02      100
            ...
            02:20      84
            02:21      88

所以我有一个循环,从我的开始日期到结束日期循环,检查每一步是否需要从数据库下载其他价格。

   List<Price> PriceList = Database.GetPrices(first years worth or prices) 

   for(DateTime dtNow = dtStart; dtNow < dtEnd; dtNow = dtNow.AddMinutes(1))
   {
         // run some calculations which doesn't take long

         // then check if PriceList[i] == PriceList.Count - 1
         // if so get more prices from the database and obviously reset i to zero but baring in mind I need to keep the previous 10 prices
   }

这类问题的最佳解决方案是什么?我应该从另一个线程上的数据库中获取价格吗?

3 个答案:

答案 0 :(得分:2)

让我们做一些数学

class Price
{
    DateTime DatePrice;
    double Open;
    double High;
    double Low;
    double Close;
}

的成员大小为8(DateTime)+ 4 * 8(double)= 40。由于它是一个引用类型,您需要一个方法表指针和一个另外添加16个字节的SyncBlock指针。由于你需要保持指向对象的指针(x64上的8个字节),我们得到每个64字节实例的总大小。

如果你想拥有10年历史,拥有3,7百万个实例,你将需要237 MB的内存,这在当今世界并不多。

你可以通过从double转换为浮点数来减少一些开销,浮点数只需要4个字节,如果你使用结构

struct Price
{
    DateTime DatePrice;
    float Open;
    float High;
    float Low;
    float Close;
}

由于股票的价值范围不是很高而且您对长期趋势或模式感兴趣而不是0,000000x分数,因此您只需要24个字节且没有大的精度损失。

使用这个结构,您的10年时间范围将只花费88MB,并且它将使垃圾收集器保持关闭数据,因为它对于GC是不透明的(结构中没有引用类型)。

对于时间范围来说,即使使用当今的计算机和内存大小,这种简单的优化也应该足够好。它甚至可以放入x86地址空间,但我建议在x64上运行它,因为我怀疑你不仅要检查一个库存,还要检查几个并行的库存。

答案 1 :(得分:1)

如果我是你,我会保留缓存问题(这似乎是你的问题),与功能分开。

我不知道你目前是如何从数据库中获取数据的。我猜你正在使用类似于

的逻辑
^[!-~]{1,50}$

您可以使用下面的内容逐步获取并将获取的行转换为DataAdapter.Fill(dataset); List<Price> PriceList = dataset.Tables[0].SomeLinqQuery(); 对象

,而不是同时获取所有价格。
Price

现在要对价格进行透明访问,您可能想要在一些可以提供缓存的类中滚动

IDataReader rdr = IDbCommand.ExecuteReader();
while(rdr.Read())
{
}

一旦拥有了这样的基础架构,那么您几乎可以使用它来限制加载定价信息的数量,并将您的功能与缓存机制分开。这使您可以灵活地控制预备价格所需的内存量以及可以处理的数据量

毋庸置疑,这只是一个指导原则 - 您必须了解这一点并自行实施

答案 2 :(得分:0)

从时间效率的角度来看,最优惠的是您获得最初的一批价格,开始处理这些价格,然后立即开始检索其余的价格。在处理过程中检查新数据的问题是,每次需要新数据时都必须延迟程序。

如果您真的关心内存,那么您需要做的就是从完成后的清单中删除价格。这将允许垃圾收集器释放消耗的内存。否则,如果您的计划完成并且您退回去年的价格,您将获得所有价格,并且您将消耗尽可能多的内存,就像您一次获得所有价格一样。

我相信你对记忆的担忧还为时过早。我唯一不得不担心.net中的内存/垃圾收集器是我长时间运行的过程,并且该过程中的一步包括下载PDF。即使我根据需要检索了PDF,内存中的PDF最终会在运行一段时间后占用GB的内存,并且在消耗了列表的.net内存限制后抛出异常。