使用 C# 对巨大的二进制文件进行排序

Posted

技术标签:

【中文标题】使用 C# 对巨大的二进制文件进行排序【英文标题】:Sorting gigantic binary files with C# 【发布时间】:2011-11-28 03:03:18 【问题描述】:

我有一个大约 400 GB 大小的大文件。由外部封闭系统每天生成。它是一个二进制文件,格式如下:

byte[8]byte[4]byte[n]

其中 n 等于 byte[4] 的 int32 值。

此文件没有分隔符,要读取整个文件,您只需重复直到 EOF。每个“项目”表示为 byte[8]byte[4]byte[n]。

文件看起来像

byte[8]byte[4]byte[n]byte[8]byte[4]byte[n]...EOF

byte[8] 是一个 64 位数字,表示由 .NET Ticks 表示的时间段。我需要对这个文件进行排序,但似乎找不到最快的方法。

目前,我将 Ticks 加载到一个结构中,并将 byte[n] 开始和结束位置并读取到文件的末尾。之后,我按 Ticks 属性对内存中的 List 进行排序,然后打开 BinaryReader 并按 Ticks 顺序查找每个位置,读取 byte[n] 值,然后写入外部文件。

在这个过程的最后,我得到了一个排序的二进制文件,但它需要永远。我正在使用 C# .NET 和一个非常强大的服务器,但磁盘 IO 似乎是个问题。

服务器规格:

2x 2.6 GHz Intel Xeon(Hex-Core with HT)(24 线程) 32GB 内存 500GB RAID 1+0 2TB RAID 5

我浏览了整个互联网,只能找到一个大文件为 1GB 的示例(让我发笑)。

有人有什么建议吗?

【问题讨论】:

我不确定我是否理解您如何在只有 500GB RAID 磁盘的系统上读取一个 400GB 文件并写入另一个已排序的 400GB 文件,但 Greg 的建议听起来不错,尽管我没有亲自处理这么大的文件。 嘿,我还有 2TB RAID 5。 【参考方案1】:

加速这种文件访问的好方法是memory-map the entire file into address space,让操作系统负责从文件中读取它需要的任何位。所以做你现在做的同样的事情,除了从内存中读取而不是使用BinaryReader/seek/read。

你有很多主内存,所以这应该提供相当好的性能(只要你使用 64 位操作系统)。

【讨论】:

感谢您的快速回复! MemoryMappedFile 是否能够处理 400GB 的文件?我是否需要创建不同类型的视图访问器(随机访问与顺序)?再次感谢! :) MemoryMappedFile 应该能够一次性处理所有文件。我已经在 FreeBSD 上使用 Python 完成了这项工作,文件大小为 30 GB,但我完全希望它可以在 Windows 上以您的文件大小正常工作。我不确定访问器之间的区别,但任何一个都可能工作。您将按顺序读取文件一次,然后在排序后以随机顺序读取它。【参考方案2】:

使用归并排序。 它是在线的并且很好地并行化。

http://en.wikipedia.org/wiki/Merge_sort

【讨论】:

【参考方案3】:

如果您可以学习 Erlang 或 Go,它们可能会非常强大并且扩展性非常好,因为您有 24 个线程。利用异步 I/O。合并排序。 由于您有 32GB 的 RAM,请尝试将尽可能多的加载到 RAM 中并在那里对其进行排序,然后再写回磁盘。

【讨论】:

【参考方案4】:

我会分几次这样做。在第一次通过时,我会创建一个刻度列表,然后将它们均匀地分配到许多(数百个?)桶中。如果您提前知道刻度是均匀分布的,则可以跳过此初始通道。在第二次通过时,我会将记录拆分为这几百个大小大致相同的单独文件(这些小得多的文件代表按您想要的顺序排列的刻度组)。然后我会在内存中分别对每个文件进行排序。然后连接文件。

它有点类似于哈希排序(我认为)。

【讨论】:

以上是关于使用 C# 对巨大的二进制文件进行排序的主要内容,如果未能解决你的问题,请参考以下文章

带有 -L/usr/local/lib 的巨大可执行文件大小

c# 将字符串以二进制形式写入文件

删除 C# 二进制文件中未使用的预处理器符号

怎么用C#合并五个bin文件并排序?

C#读写二进制文件

Java中排序(内存映射?)文件中的二进制搜索