Stream.CopyTo的更快/管道版本

时间:2017-07-06 01:33:28

标签: c# .net performance

我很震惊地看到Stream.CopyTo的默认实现没有用管道输入。没有管道意味着在写入目的地时没有读取源。

这意味着在大多数情况下,性能是其可能性的一半。 (将文件流从C:复制到另一个HDD)

如何实现快速管道CopyTo?

2 个答案:

答案 0 :(得分:1)

文件直接从操作系统进行顺序预读和写缓冲。在这些情况下,您不太可能提高最基本Copy实施的效果。

TCP套接字同样会为您缓冲相当多的数据,因此只要您的协议允许批量数据发送,基本Copy对他们来说应该没问题。

当你以无缓冲的方式打开文件时(非常罕见 - 如果你可以执行自己的缓存,你知道它会比操作系统更好),或者设计不良的网络协议就是执行你自己的特殊缓冲可能会带来相当大的改进。

在大多数情况下,这样的事情就足够了......

static async Task CopyMaybeFaster(Stream src, Stream dst)
{
    byte[] buffer = new byte[65536];
    int curoff = 0;

    Task<int> readTask = src.ReadAsync(buffer, curoff, 32768);
    Task writeTask = Task.CompletedTask;
    int len;

    while ((len = await readTask.ConfigureAwait(false)) != 0)
    {
        await writeTask.ConfigureAwait(false);
        writeTask = dst.WriteAsync(buffer, curoff, len);

        curoff ^= 32768;
        readTask = src.ReadAsync(buffer, curoff, 32768);
    }

    await writeTask.ConfigureAwait(false);
}

答案 1 :(得分:-2)

这是我能想到的最强大的实现:

public void CopyTo_Fast(Stream Destination, int Buffersize)
{
    byte[] BufferA = new byte[Buffersize];
    byte[] BufferB = new byte[Buffersize];
    int Abytes = Read(BufferA, 0, BufferA.Length);//read first buffer
    if (Abytes < Buffersize) //trivial case stream too small for two buffers
    {
        Destination.Write(BufferA, 0, Abytes);
        return;
    }
    int Bbytes = 0;
    Task Writer;
    Task Reader;

    while(true) //Read to end
    {
        Writer = Task.Run(() => Destination.Write(BufferA, 0, Abytes));
        Reader = Task.Run(() => Bbytes = Read(BufferB, 0, Buffersize));
        Task.WaitAll(Writer, Reader);
        if (Bbytes == 0) return;

        Writer = Task.Run(() => Destination.Write(BufferB, 0, Bbytes));
        Reader = Task.Run(() => Abytes = Read(BufferA, 0, Buffersize));
        Task.WaitAll(Writer, Reader);
        if (Abytes == 0) return;
    }
}