何时使用.NET BufferedStream类?

Jad*_*ias 52 .net performance buffer

MSDN网站上指出:

缓冲区是内存中用于缓存数据的字节块,从而减少了对操作系统的调用次数.缓冲区可提高读写性能.缓冲区可用于读取或写入,但不能同时使用.BufferedStream的Read和Write方法自动维护缓冲区.

我应该在每个可能的场合使用这门课吗?

dew*_*ald 70

据布拉德艾布拉姆斯说,几乎从来没有:链接

不,围绕FileStream包装BufferedStream没有任何好处.我们大约4年前将BufferedStream的缓冲逻辑复制到FileStream中以鼓励更好的默认性能......事实上,我认为.NET Framework中没有任何Streams需要它,但是自定义Stream实现可能需要它.他们默认不做缓冲.

  • -1`FileStream`只是其中一个.net流,并且将单个字节写入`NetworkStream`,它不会覆盖`Stream`的通用`WriteByte()`方法,效率非常低.`BufferedStream`将极大地提升这个和其他用例的性能(参见@idn的回答) (15认同)

hac*_*ker 20

以下是我正在参加的在线课程的一些文字:

BufferedStream类是一个扩展Stream类的具体类,用于为其他类型的流提供额外的内存缓冲区,包括同步和异步.必须将BufferedStream类配置为在创建类的实例时读取或写入,但不能将BufferedStream配置为同时执行这两个任务.

Microsoft通过包含内置缓冲区来提高.NET Framework中所有流的性能.通过将BufferedStream应用于现有流(例如FileStream或MemoryStream),性能得到显着提高.将BufferedStream应用于现有.NET Framework流会产生双缓冲区.

BufferedStream类的最常见应用是在不包含内置缓冲区的自定义流类中.

  • 我无法理解将缓冲区应用于`MemoryStream`,因为它本质上已经是内存中一个巨大的`byte []`缓冲区.句子"通过将BufferedStream应用于现有流(例如FileStream或MemoryStream)而显着提高的性能"似乎不完整,并且可能缺少否定词"不是".想知道这是否实际上是一个误导性的答案...... (51认同)

小智 19

我知道的最好的情况是BinaryFormatter直接从NetworkStream序列化/反序列化.在其间使用BufferedStream可将性能提高十倍.


Ton*_*Nam 6

迟到的答案,但无论如何都会回答。如果您了解它的工作原理,您就可以理解为什么在某些情况下使用它没有意义,以及为什么在其他情况下不使用它是有意义的。

为了证明这一点,我正在使用StreamWrapper类。仅使用该类来查看有多少次遇到断点!在这个类中各处放置断点。我们的目标是查看调用 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);
    }


}
Run Code Online (Sandbox Code Playgroud)

现在您已经拥有了基本上是现有 strem 的包装器的包装器类,您可以执行以下测试:

写作示例:

// 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
}
Run Code Online (Sandbox Code Playgroud)

示例阅读:

// 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] );

        }
    }
}
Run Code Online (Sandbox Code Playgroud)

例如,如果要对文件进行写入操作,则可以通过确保每次至少写入 8 个字节而不是 1 个字节来提高性能。在现实生活中,您希望这个数字约为 4 KB


GvS*_*GvS 5

普通文件 I/O 流已使用 StreamReader/StreamWriter 进行缓冲。

由于流上的读/写操作通常使用采用字节数组的读/写方法,因此您自然会自己提供一些缓冲。

如果您使用非常小的数组或使用 WriteByte,则通过在其间使用 BufferedStream 可能会获得更好的性能。