Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/276.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 存储大型查找表_C#_Performance_Optimization_Serialization_Lookup Tables - Fatal编程技术网

C# 存储大型查找表

C# 存储大型查找表,c#,performance,optimization,serialization,lookup-tables,C#,Performance,Optimization,Serialization,Lookup Tables,我正在开发一个应用程序,它利用非常大的查找表来加速数学计算。这些表中最大的是一个int[],它有大约1000万个条目。并非所有查找表都是int[]。例如,一本词典有大约200000个词条。目前,我生成每个查找表一次(需要几分钟),并使用以下代码段将其序列化到磁盘(压缩): int[] lut = GenerateLUT(); lut.Serialize("lut"); 其中Serialize的定义如下: public static void Serialize(this

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

    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");
 ...
起初,我认为可能是gzip压缩导致了速度的减慢,但删除它只需要从序列化/反序列化例程中略过几百毫秒


有谁能建议在应用程序初始启动时加快这些查找表的加载时间吗?

我想最明显的建议是在后台加载它们。一旦应用程序启动,用户打开了他们的项目,并选择了他们想要的任何操作,就不会有太多的15秒等待了。

首先,在后台线程中反序列化将防止应用程序在这种情况下“挂起”。光是这一点就足以解决你的问题

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


这就是说,根据其使用模式,数据库可能是更好的方法。您始终可以制作更面向数据库的内容,并从DB以惰性方式构建查找表(即:查找是LUT中的查找,但如果查找不存在,请从DB加载它并将其保存在表中)。这将使启动瞬间(至少在LUT方面)完成,并且可能仍然保持快速查找。

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

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


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

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

这可能会降低应用程序的实际性能,因为每次计算都需要读取磁盘。但这将使应用程序的感知性能大大提高,因为从来没有长时间的等待。不管你喜欢与否,感知可能比现实更重要。

为什么要压缩它们

磁盘比RAM大


直接读取二进制文件应该很快。

我同意这一点,但在我看来,这仍然是一种解决办法。对于我的应用程序,gui非常简单,用户可以在不到5秒钟的时间内完成计算。因此,目前,我正在争取5秒或更短的加载时间(其中查找表将在不到5秒的时间内在后台加载)。查找表磁盘上的总数据小于100 MB,因此我认为可以排除数据传输限制。
    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);
            }
        }
    }