MSDN网站中指出:
缓冲区是内存中的一块字节块,用于缓存数据,从而减少对操作系统的调用次数。缓冲区可以提高读写性能。缓冲区可用于读取或写入,但不能同时进行。BufferedStream类的Read和Write方法会自动维护缓冲区。
我应该在任何可能的情况下都使用这个类吗?
MSDN网站中指出:
缓冲区是内存中的一块字节块,用于缓存数据,从而减少对操作系统的调用次数。缓冲区可以提高读写性能。缓冲区可用于读取或写入,但不能同时进行。BufferedStream类的Read和Write方法会自动维护缓冲区。
我应该在任何可能的情况下都使用这个类吗?
根据 Brad Abrams 的说法,几乎不需要: 链接
不需要在 FileStream 外面包装 BufferedStream,没有任何好处。事实上,我们大约4年前将 BufferedStream 的缓存逻辑复制到了 FileStream 中,以鼓励更好的默认性能... 实际上,我认为.NET Framework 中没有任何 Stream 需要它,但如果自定义的 Stream 实现不会默认进行缓存,则可能需要它。
我知道的最佳情况是使用BinaryFormatter直接从NetworkStream进行序列化/反序列化。在其中使用BufferedStream可以将性能提升十倍。
BufferedStream类是一个具体的类,它扩展了Stream类,并用于为另一种类型的流提供附加的内存缓冲区,可以同步和异步使用。创建类的实例时,必须将BufferedStream类配置为读取或写入其中之一,但不能将其配置为同时执行两个任务。
Microsoft通过包含内置缓冲区来改善了.NET Framework中所有流的性能。通过将BufferedStream应用于现有流,例如FileStream或MemoryStream,性能显着提高。将BufferedStream应用于现有的.NET Framework流会导致双重缓冲区。
BufferedStream类最常见的应用程序是在不包括内置缓冲区的自定义流类中。
MemoryStream
应用缓冲区,因为它本质上已经是一个大的byte[]
内存缓冲区。句子“The performance noticeably improved by applying a BufferedStream to existing streams, such as a FileStream or MemoryStream”似乎不完整,也许缺少否定词“不是”。我想知道这是否是一个误导性的回答... - Drew NoakesStream
概念的通用性质正是导致它无法控制其内容布局和访问方式的原因,而这正是需要(详细到极致)了解的唯一信息,才能开始产生任何可衡量的内存总线性能差异。更现实的问题是最坏情况下读/写 100% 故障/脏虚拟内存页面的“内存访问”,但即使在这种情况下,Stream
也无法超越操作系统的页面故障机制。 - Glenn SlaydenGZipStream
应该考虑使用BufferedWriter
。 来源:https://github.com/dotnet/runtime/issues/39233#issuecomment-745572680 - leon.ioStreamWrapper
类。只使用这个类来查看你打了多少次断点!在这个类上放置断点。我们的目标是看到我们调用Write、Read和其他方法的次数。// This class is only used for demo purposes. Place a breakpoint on all parts
class StreamWrapper : Stream
{
Stream stream;
public StreamWrapper(Stream s)
{
stream = s;
}
public override bool CanRead => stream.CanRead;
public override bool CanSeek => stream.CanSeek;
public override bool CanWrite => stream.CanWrite;
public override long Length => stream.Length;
public override long Position { get => stream.Position; set => stream.Position = value; }
public override void Flush()
{
stream.Flush();
}
public override int Read(byte[] buffer, int offset, int count)
{
return stream.Read(buffer, offset, count);
}
public override long Seek(long offset, SeekOrigin origin)
{
return stream.Seek(offset,origin);
}
public override void SetLength(long value)
{
stream.SetLength(value);
}
public override void Write(byte[] buffer, int offset, int count)
{
stream.Write(buffer, offset, count);
}
}
现在您有了一个基本上是现有流的包装器类,您可以进行以下测试:
示例写法:
// in real life you want this to be larger.
int bufferSize = 8;
// Use BufferedStream to buffer writes to a MemoryStream.
using (var memory_test = new StreamWrapper(new MemoryStream()))
using (BufferedStream stream = new BufferedStream(memory_test, bufferSize))
{
// all this will only send one write to memory_test!
stream.Write(new byte[] { 1, 2 });
stream.Write(new byte[] { 1, 2 });
stream.Write(new byte[] { 1, 2 });
stream.Write(new byte[] { 1, 2 });
// BREAKPOINT ONLY HITS ONE TIME
// All this will also send only one write to memory_test
for (int i = 0; i < 8; i++)
stream.WriteByte(5);
// BREAKPOINT ONLY HITS ONE TIME AGAIN INSTAD OF 8
// this will send one write to memory_test. Writes of more than 8 bytes can happen!
stream.Write(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18 });
// ALL THIS WILL SEND ONE WRITE AGAIN
}
示例阅读:
// example reading
{
// create stream with some data in it that we will be reading
var ms = new MemoryStream();
{
// Write 256 bytes
for (int i = 0; i <= byte.MaxValue; i++)
{
ms.WriteByte((byte)i);
}
ms.Position = 0;
}
// Use BufferedStream to buffer writes to a MemoryStream.
using (var memory_test = new StreamWrapper(ms))
{
using (BufferedStream stream = new BufferedStream(memory_test, 8))
{
// note I do not care about the output of each read for demo breakpoint. On real life you will store that output
// for now we only care how many times we hit the breakpoint because on real life that could be a slow/expensive
// operation such as opening a file for writing and you want to do those as few as possible.
// hit breakpoint only one time with all this reads
stream.ReadByte();
stream.ReadByte();
stream.ReadByte();
stream.ReadByte();
stream.ReadByte();
stream.ReadByte();
stream.ReadByte();
stream.ReadByte();
// hit breakpoint only one time with all this reads
stream.Read(new byte[2] );
stream.Read(new byte[2] );
stream.Read(new byte[2] );
stream.Read(new byte[2] );
// hit breakpoint only one time even though it is larger than our buffer size 8
// our goal is to hit the breakpoint as fewest time as possible because in real life
// this could be slow/expensive operations
stream.Read(new byte[1024] );
}
}
}
普通的文件I/O流已经使用StreamReader/StreamWriter进行了缓冲。
由于流上的读写操作通常使用接受字节数组的Read/Write方法,您自然会提供一些缓冲。
如果您使用非常小的数组或使用WriteByte,则通过在中间使用BufferedStream可能会获得更好的性能。
在任何可能的场合都必须使用常识。当对内存流进行读写时,使用这个类是没有用的,但是在进行网络或磁盘IO时可能会非常有用(如果这些子系统的流没有自己进行缓冲)。
NetworkStream
感兴趣。我想删除一些缓冲代码,因为我相当确定它们没有必要。我会继续浏览! - Drew NoakesNetworkStream
的情况下,您只能使用byte[]
进行读写,因此缓冲由调用者控制。 NetworkStream
也不可寻址,因此不需要内部缓冲区。 - Drew Noakes
FileStream
只是 .net 流之一,例如将单个字节写入NetworkStream
,由于它未覆盖Stream
的通用WriteByte()
方法,因此效率非常低。BufferedStream
可以极大地提高此类用例的性能(请参见@idn的答案)。 - Evgeniy BerezovskyFileStream
,如果在FileStream
之后存在操作(例如FileStream
->解压缩
->BufferedStream
),下游缓冲仍然会带来好处,其中解压缩
步骤不会在内部缓冲。 - playstedBufferedStream
今天剩余的用途实际上是替换而不是包装那些具体的Stream
实现,以便调整、微调或修复该“逻辑”中的任何缺陷。尤其是,具体流的内部**Flush
**行为无法更改,在certain scenarios中可能不理想。 - Glenn SlaydenBufferedSteam
在某些情况下确实对性能产生了积极影响。这似乎是一个被引入的 bug,但是当使用GZipStream
时,它看起来是有效的。来源:https://github.com/dotnet/runtime/issues/39233#issuecomment-745572680 - leon.io