存储大型查找表

时间:2009-07-20 23:50:56

标签: c# performance optimization serialization lookup-tables

我正在开发一个应用程序,它利用非常大的查找表来加速数学计算。这些表中最大的是一个int [],它有大约1000万个条目。并非所有查找表都是int []。例如,一个是包含约200,000个条目的词典。目前,我使用以下代码段生成每个查找表一次(需要几分钟)并将其序列化为磁盘(带压缩):

    int[] lut = GenerateLUT();
    lut.Serialize("lut");

其中Serialize的定义如下:

    public static void Serialize(this object obj, string file)
    {
        using (FileStream stream = File.Open(file, FileMode.Create))
        {
            using (var gz = new GZipStream(stream, CompressionMode.Compress))
            {
                var formatter = new BinaryFormatter();
                formatter.Serialize(gz, obj);
            }
        }
    }

我遇到的烦恼是启动应用程序时,这些查找表的反序列化需要很长时间(超过15秒)。这种类型的延迟会使用户烦恼,因为在加载所有查找表之前,应用程序将无法使用。目前反序列化如下:

     int[] lut1 = (Dictionary<string, int>) Deserialize("lut1");
     int[] lut2 = (int[]) Deserialize("lut2");
 ...

其中反序列化定义为:

    public static object Deserialize(string file)
    {
        using (FileStream stream = File.Open(file, FileMode.Open))
        {
            using (var gz = new GZipStream(stream, CompressionMode.Decompress))
            {
                var formatter = new BinaryFormatter();
                return formatter.Deserialize(gz);
            }
        }
    }

起初,我认为可能是导致速度减慢的gzip压缩,但删除它只是从序列化/反序列化例程中略过了几百毫秒。

有人建议在应用初次启动时加快这些查找表的加载时间吗?

5 个答案:

答案 0 :(得分:2)

首先,在后台线程中反序列化将阻止应用程序在发生这种情况时“挂起”。仅这一点就足以解决你的问题了。

然而,一般而言,序列化和反序列化(特别是大型词典)往往非常慢。根据数据结构的不同,编写自己的序列化代码可以大大加快这一速度,尤其是在数据结构中没有共享引用的情况下。

话虽如此,根据其使用模式,数据库可能是更好的方法。您总是可以创建更加面向数据库的东西,并从DB中以惰性方式构建查找表(即:查找是在LUT中查找,但如果查找不存在,则从DB加载并保存它在表中)。这将使启动瞬间完成(至少在LUT方面),并且可能仍然保持查找相当活泼。

答案 1 :(得分:0)

我们在这里谈论的数据有多少?根据我的经验,从磁盘读取一块千兆字节到内存大约需要20秒。因此,如果您的读数超过半千兆字节,那么您几乎肯定会遇到硬件限制。

如果数据传输速率不是问题,那么实际的反序列化需要时间。如果您有足够的内存,则可以将所有表加载到内存缓冲区(使用File.ReadAllBytes()),然后从内存流中反序列化。这将允许您确定读取的时间,以及反序列化的时间。

如果反序列化需要花费很多时间,那么如果你有多个处理器,你可以生成多个thred来并行进行序列化。使用这样的系统,您可能会在为另一个表加载数据时反序列化一个或多个表。这种流水线方法可以使您的整个加载/反序列化时间几乎与仅加载一样快。

答案 2 :(得分:0)

另一种选择是将表格放入表:真实数据库表中。甚至像Access之类的引擎也应该产生相当好的性能,因为每个查询都有一个明显的索引。现在,应用程序只需要在实际使用数据时读取数据,即使这样,它也会确切地知道在文件中查找的位置。

这可能会使应用程序的实际性能降低一些,因为您必须为每次计算执行磁盘读取。但它会使应用程序的感知性能更好,因为从不漫长的等待。并且,无论喜欢与否,感知可能比现实更重要。

答案 3 :(得分:0)

为什么拉链?

磁盘大于RAM。

直接二进制读取应该非常快。

答案 4 :(得分:0)

我想明显的建议是在后台加载它们。一旦应用程序启动,用户已经打开了他们的项目,并选择了他们想要的任何操作,剩下15秒钟就没有多少时间等待。