3

我需要将字节写入IEnumerable<byte>文件。
我可以将其转换为数组并使用Write(byte[])方法:

using (var stream = File.Create(path))
    stream.Write(bytes.ToArray());

但由于IEnumerable不提供集合的项目数,除非绝对必要,否则ToArray不建议使用。

所以我可以在每次迭代中迭代IEnumerable和使用:WriteByte(byte)

using (var stream = File.Create(path))
    foreach (var b in bytes)
        stream.WriteByte(b);

我想知道在写入大量数据时哪个会更快。

我猜使用Write(byte[])根据数组大小设置缓冲区,因此在涉及数组时会更快。

我的问题是,当我只有一个IEnumerable<byte>具有 MB 数据的数据时,哪种方法更好?将其转换为数组并调用Write(byte[])或迭代它并WriteByte(byte)为每个调用?

4

2 回答 2

3

枚举大量字节流是一个过程,它为通常很便宜的事情增加了大量开销:将字节从一个缓冲区复制到下一个缓冲区。

通常,LINQ 样式的开销并不重要,但是当涉及到在普通硬盘驱动器上每秒处理 1 亿字节时,您注意到严重的开销。这不是过早的优化。我们可以预见这将是一个性能热点,因此我们应该积极优化。

IEnumerable因此,在您周围复制字节时,可能根本不应该依赖抽象IList。传递ArraySegement<byte>也包含Offsetand的数组 or Count。这使您不必过于频繁地对数组进行切片。

具有高吞吐量 IO 的一件事也是致命的,那就是每个字节调用一个方法。就像按字节读取和按字节写入一样。这会降低性能,因为这些方法必须每秒调用数亿次。我亲身经历过。

始终一次处理至少 4096 字节的整个缓冲区。根据您使用的 IO 媒体,您可以使用更大的缓冲区(64k、256k 甚至兆字节)。

于 2012-09-22T17:51:19.950 回答
1

您应该分析哪个版本更快。该类FileStream有一个内部缓冲区,可以将Read()Write()方法与实际的文件系统访问解耦。

如果您未在FileStream构造函数中指定缓冲区大小,则默认情况下它使用类似 4096 字节的缓冲区。该缓冲区会将您的许多WriteByte()调用组合成对基础文件的一次写入。唯一的问题是WriteByte()调用的开销是否会超过调用的开销Enumerable.ToArray()。后者肯定会使用更多内存,但您总是必须处理这种权衡。

仅供参考:当前的 .NET 4 实现Enumerable.ToArray()涉及通过在必要时复制其大小来增长数组。每次增长时,都会复制所有值。此外,当所有项目都存储在数组中时,其内容会再次复制到最终大小的数组中。对于IEnumerable<T>实际实现的实例,ICollection<T>代码利用这一事实从正确的数组大小开始,并让集合改为复制。

于 2012-09-22T18:14:31.293 回答