7

目前,我正在开展一个项目,我需要将 GB 的数据带到客户端机器上以执行某些任务,并且该任务需要整个数据,因为它对数据进行一些分析并有助于决策过程。

所以问题是,在不影响客户端机器和应用程序性能的情况下,将大量数据管理到内存中的最佳实践和合适方法是什么。

注意:在应用程序加载时,我们可以花时间将数据从数据库带到客户端机器,这在我们的案例中是完全可以接受的。但是一旦数据在启动时加载到应用程序中,性能就非常重要。

4

1 回答 1

15

如果没有问题陈述,即您当前面临什么问题,这有点难以回答,但以下只是一些想法,基于我们最近在类似情况下的一些经验。然而,要改变这种类型的模型需要做很多工作——所以这也取决于你可以投入多少来尝试“修复”它,我不能保证“你的问题”与“我们的问题”是一样的问题”,如果你明白我的意思。因此,如果以下方法对您不起作用,请不要生气!


将这么多数据加载到内存中总是会产生一些影响,但是,我想我明白你在做什么......

当天真地加载这么多数据时,您将拥有许多(数百万?)对象和类似或更多数量的引用。你显然会想要使用 x64,所以引用会加起来——但就性能而言,最大的问题将是垃圾收集。您有很多无法收集的对象,但是 GC 会知道您正在使用大量内存,并且无论如何都会定期尝试。这是我在这里更详细地查看的内容,但下图显示了影响 - 特别是,那些“尖峰”都是 GC 杀死性能:

http://marcgravell.blogspot.co.uk/2011/10/assault-by-gc.html

对于这种情况(大量数据加载,从未释放),我们切换到使用 structs,即将数据加载到:

struct Foo {
    private readonly int id;
    private readonly double value;
    public Foo(int id, double value) {
        this.id = id;
        this.value = value;
    }
    public int Id {get{return id;}}
    public double Value {get{return value;}}
}

并将它们直接存储在数组(不是列表)中:

Foo[] foos = ...

这样做的意义在于,因为其中一些结构非常大,我们不希望它们在堆栈上多次复制自己,但是使用数组你可以这样做:

private void SomeMethod(ref Foo foo) {
     if(foo.Value == ...) {blah blah blah}
}
// call ^^^
int index = 17;
SomeMethod(ref foos[index]);

请注意,我们直接传递了对象——它从未被复制;foo.Value实际上是直接查看数组内部。当您需要对象之间的关系时,棘手的部分就开始了。您不能在此处存储引用,因为它是 a struct,并且您不能存储它。但是,您可以做的是存储索引(到数组中)。例如:

struct Customer {
      ... more not shown
      public int FooIndex { get { return fooIndex; } }
}

不如 方便customer.Foo,但以下效果很好:

Foo foo = foos[customer.FooIndex];
// or, when passing to a method, SomeMethod(ref foos[customer.FooIndex]);

关键点:

  • 我们现在使用“引用”的一半大小(anint是 4 个字节;x64 上的引用是 8 个字节)
  • 我们在内存中没有几百万个对象头
  • 我们没有巨大的对象图供 GC 查看;只有少数数组可以让 GC快速查看
  • 但是使用起来不太方便,加载时需要一些初始处理

补充说明:

  • 弦乐是杀手;如果你有数百万个字符串,那就有问题了;至少,如果您有重复的字符串,请确保您进行一些自定义实习(不是string.Intern,那会很糟糕)以确保每个重复值只有一个实例,而不是 800,000 个具有相同内容的字符串
  • 如果您有有限长度的重复数据,而不是子列表/数组,您可以考虑使用fixed数组;这需要unsafe代码,但避免了另外无数的对象和引用

作为一个额外的脚注,对于这么多的数据,您应该非常认真地考虑您的序列化协议,即您如何通过网络发送数据。我强烈建议远离XmlSerializer,DataContractSerializerBinaryFormatter. 如果您需要有关此主题的指示,请告诉我。

于 2012-09-13T07:21:44.560 回答