C# Stream.CopyTo的更快/管道版本

C# Stream.CopyTo的更快/管道版本,c#,.net,performance,C#,.net,Performance,看到Stream.CopyTo的默认实现没有管道传输,我感到震惊。“无管道”表示在写入目标时未读取源 这意味着,在大多数情况下,性能是可能的一半。(将Filestream从C:复制到另一个硬盘) 如何实现快速管道复制目标?这是我能想到的最健壮的实现: public void CopyTo_Fast(Stream Destination, int Buffersize) { byte[] BufferA = new byte[Buffersize]; byte[] BufferB

看到Stream.CopyTo的默认实现没有管道传输,我感到震惊。“无管道”表示在写入目标时未读取源

这意味着,在大多数情况下,性能是可能的一半。(将Filestream从C:复制到另一个硬盘)


如何实现快速管道复制目标?

这是我能想到的最健壮的实现:

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;
    }
}
public void CopyTo_Fast(流目的地,int Buffersize)
{
byte[]BufferA=新字节[Buffersize];
byte[]BufferB=新字节[Buffersize];
int Abytes=Read(BufferA,0,BufferA.Length);//读取第一个缓冲区
if(AbytesDestination.Write(BufferA,0,Abytes));
Reader=Task.Run(()=>Bbytes=Read(BufferB,0,Buffersize));
Task.WaitAll(作者、读者);
如果(b字节==0)返回;
Writer=Task.Run(()=>Destination.Write(BufferB,0,Bbytes));
Reader=Task.Run(()=>Abytes=Read(BufferA,0,Buffersize));
Task.WaitAll(作者、读者);
如果(Abytes==0)返回;
}
}

文件直接从操作系统进行顺序预读和写入缓冲。在这些情况下,您不太可能提高最基本的
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);
}
静态异步任务copyMaybeaster(Stream src,Stream dst)
{
字节[]缓冲区=新字节[65536];
int curoff=0;
Task readTask=src.ReadAsync(缓冲区,curoff,32768);
Task writeTask=Task.CompletedTask;
内伦;
while((len=await readTask.ConfigureAwait(false))!=0)
{
wait writeTask.configurewait(false);
writeTask=dst.WriteAsync(缓冲区、curoff、len);
curoff^=32768;
readTask=src.ReadAsync(缓冲区,curoff,32768);
}
wait writeTask.configurewait(false);
}

这是一个非常糟糕的建议-同步代码上的异步包装不是一个好的做法(特别是当存在真正的异步选项时),等待任务可能会导致死锁。此外,OP还未经证实地宣称.net版本很慢,但这个答案根本不包括性能部分。需要同步管道版本的多线程。所有示例都使用旧的BeginRead和EndRead语法。这更容易维护。我将添加性能差异和异步版本。此代码也没有死锁。此代码不是管道传输的。你在这里所做的就是在读的时候等待,在写的时候等待。控件返回给调用方,它不会同时读写。这一点也不快。定义任务不会执行它。您在任何时候都只等待一项任务。如果在两个任务上都使用Task.WaitAll,则代码将被管道传输。您对任务的理解有缺陷
ReadAsync
WriteAsync
返回已启动的热任务
Wait
只是等待任务完成,它不会启动/运行任务。这段代码与CopyToAsync方法的功能类似吗?@FrederikHeysels有两个操作同时进行,每个操作使用一半缓冲区。另外,感谢您注意到
WriteAsync
指向错误的流--已修复。