C#将大数组序列化到磁盘上

3

我有一个非常大的图形数据存储在一维数组中(约为1.1 GB),我可以将其存储在我的机器上,该机器运行Windows XP,具有2GB的RAM和2GB的虚拟内存。我能够在内存中生成整个数据集,但是当我尝试使用BinaryFormatter将其序列化到磁盘时,文件大小达到约50MB,然后出现内存不足异常。我使用的写入代码与我在所有较小问题中使用的代码相同:

StateInformation[] diskReady = GenerateStateGraph();
BinaryFormatter bf = new BinaryFormatter();
using (Stream file = File.OpenWrite(@"C:\temp\states.dat"))
{
    bf.Serialize(file, diskReady);
}

这个搜索算法非常轻量级,一旦它在内存中,我能够毫无问题地在图上进行搜索。

我有三个问题:

  1. 是否有更可靠的方法将大数据集写入磁盘?我想你可以将大定义为当数据集的大小接近可用内存量时,尽管我不确定这是多么准确。

  2. 我应该转向更依赖数据库的方法吗?

  3. 有人能指引我阅读关于在C#中从磁盘文件读取大数据集部分的文献吗?

3个回答

1

自己编写文件条目。一个简单的解决方案可能是:

StateInformation[] diskReady = GenerateStateGraph();
BinaryFormatter bf = new BinaryFormatter();
using (Stream file = File.OpenWrite(@"C:\temp\states.dat"))
{
  foreach(StateInformation si in diskReady)
    using(MemoryStream ms = new MemoryStream())
    {
      bf.Serialize(ms, diskReady);
      byte[] ser = ms.ToArray();
      int len = ser.Length;
      file.WriteByte((byte) len & 0x000000FF);
      file.WriteByte((byte) (len & 0x0000FF00) >> 8);
      file.WriteByte((byte) (len & 0x00FF0000) >> 16);
      file.WriteByte((byte) (len & 0x7F000000) >> 24);
      file.Write(ser, 0, len);
    }
}

每次只需要一个StateInformation对象的内存,而要进行反序列化,则需要读取四个字节,构造长度,创建相应大小的缓冲区,填充并进行反序列化。

如果您创建更专业的格式,则可以严重优化上述所有内容的速度、内存使用和磁盘大小,但以上内容说明了原则。


这个方案可行,但在研究问题时,我们发现主要问题出在Windows 32位系统如何管理内存上。这一点变得明显,因为我们将应用程序移动到了一台具有4GB物理内存和4GB虚拟内存的机器上,而在处理过程中,完全相同的内存不足异常问题发生在同一点上。 - Nick Larsen
如果2GB的内存无法处理每个项目都小于约半GB的集合,则问题不在于可用内存的数量。有一些技巧可以获取更多的内存,但这样做永远不会像从未使用那么多内存一样高效。 - Jon Hanna

1

我处理类似这样的大型信息集合的经验是手动将其写入磁盘,而不是使用内置序列化。

这可能取决于您的StateInformation类有多复杂,如果它相当简单,您可以使用BinaryReaderBinaryWriter手动编写/读取二进制数据。这些将允许您直接将大多数值类型读取/写入流中,并按照代码指定的预期确定顺序进行。

这个选项应该能够快速读取/写入您的数据,但如果您稍后想要添加信息到StateInformation中,或者将其删除,那么这种方法就会很麻烦,因为您必须管理文件升级。


大约有600,000个状态,StateInformation基本上包含一堆标准数据类型(字符串和十进制值)。每个StateInformation的大小范围从大约1到3千字节不等。此外,一旦创建,它将永远不需要更改;数据集是完整的。 - Nick Larsen
听起来这似乎是一个不错的选择,Jon Hanna的答案类似,更像是一种折中方案,一次序列化一个对象而不是手动编写成员值。 - Ian
是的,尽管我的例子只能给出问题中所提供信息的部分示例。当我说可以使用专门的格式进行大幅度优化时,它就成为了你的解决方案。即使这样的专业化是否值得努力取决于类型的知识水平。 - Jon Hanna

0

StateInformation中包含什么?它是一个类还是结构体?

如果您只是担心易于使用的容器格式,可以轻松地将其序列化到磁盘上-创建一个类型化的DataSet,将信息存储到DataSet中,然后使用DataSet上的WriteXml()方法将其持久化到磁盘上。然后,您可以创建空的DataSet,然后使用ReadXml()将内容加载回内存。

如果StateInformation是带有值类型的结构体,则可以查看MemoryMappedFile以通过直接引用文件来存储/使用数组的内容,并将其视为内存。这种方法比DataSet复杂得多,但具有自己的一套优点。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接