在C#中从一个巨大的MemoryStream中读取数据

8
我使用BinaryReader(MemoryStream(MyByteArray))来读取可变大小的记录,并在内存中处理它们。只要我的字节流在数组中小于约1.7 GB,这样做就很好用。在那之后,你不能创建一个更大的字节数组,尽管我有足够的实际内存。因此,我的解决方案是读取字节流并将其拆分成几个字节数组。
然而,现在我无法“跨越”字节数组边界进行“读取”,并且由于我的数据是可变格式的,我无法确保字节数组总是以完整记录结束。
对于处理非常大的数据集但仍需要快速处理的人来说,这一定是一个常见问题。
我该如何处理这个问题?

你应该防止一次性将这么大的字节数组加载到内存中。是否可以实现流式解决方案,只将数组的部分加载到内存中(缓冲区)?你需要随机访问这些字节吗?或者你可以使用仅向前的解决方案,在处理时从开头到结尾读取流(而不回头)。这个字节数组来自哪里?文件,Web服务,...? - Ronald Wildenberg
2个回答

4

编辑:阅读基础知识后,我意识到对于顺序访问,内存映射文件可能比普通I/O慢。

你尝试过这样的方法吗:

var stream = new FileStream("data", 
    FileMode.Open, 
    FileAccess.Read, 
    FileShare.Read, 
    16 * 1024, 
    FileOptions.SequentialScan)

var reader = new BinaryReader(stream);

如果您的数据存储在文件中并且可以使用.NET 4.0,请考虑使用MemoryMappedFile。然后,您可以使用MemoryMappedViewStream获取流,或者使用MemoryMappedViewAccessor获取类似于BinaryReader的接口。

1
是的,我尝试过了 - 不幸的是,内存映射文件确实非常慢。 - ManInMoon

4
对于过大的数据流,您不应该尝试将其倒入MemoryStream中,而应该使用像FileStream这样的方法直接与磁盘通信。内置缓冲区通常已经足够,或者您可以使用像BufferedStream这样的方法进行调整(但我很少需要,不过,我 tend to 包括自己的数据处理缓冲区)。
您还可以考虑压缩或密集包装的数据,并使用设计为通过流记录而不是一次性创建整个图形的序列化器(尽管由于您提到了BinaryReader,您可能已经高度手动执行此操作,因此这可能不是问题)。

是的。它已经被压缩了 - 相当高效,并且我使用自己的逻辑进行反序列化。但从磁盘读取太慢了。我使用并行处理这个巨大的数据文件,并将其放在磁盘上会引起各种争用。 - ManInMoon
我希望你先读完你的300封电子邮件,否则你就没有SO甜点了! - John K
@Marc Gravell: 我知道这是一个老问题,但我也正在将大块内存读入MemoryStream中。我考虑将其更改为MemoryMappedViewStream(使用基础MemoryMappedFile)。我担心性能会受到RAM速度与磁盘速度的影响,所以我想知道您的答案是否更基于流的大小而不是流的速度。(如果您希望我将其作为新问题发布,我可以这样做。) - MrWuf
1
@MrWuf 这取决于文件的大小,你应该能够按块顺序访问映射文件,而不必将大部分内容加载到内存流中;但最终还是由你决定。 - Marc Gravell
@MarcGravell +1 感谢您的帮助!我将尝试双向流并为它们获取一些基准数据。 - MrWuf
显示剩余2条评论

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