C# 如何提高此CopyTo方法的性能?

C# 如何提高此CopyTo方法的性能?,c#,performance,io,C#,Performance,Io,编辑:我现在已经解决了这个问题。我的答案贴在下面,并将标记为解决时,让我 我有一个CopyTo(和一个CopyToAsync)方法来复制C#应用程序中的文件。 我发现,与Xcopy之类的东西相比,复制文件实际上相当慢 我提取了copy方法的核心功能,并将其放入一个测试控制台应用程序中,以获得它相对于Xcopy的运行速度,结果实际上完全不同 我得到的结果是: 异步方法:36.59秒-平均速度:1512.63 mb/秒 同步方法:36.49秒-平均速度:1516.72 mb/秒 XCOPY:5.62

编辑:我现在已经解决了这个问题。我的答案贴在下面,并将标记为解决时,让我

我有一个CopyTo(和一个CopyToAsync)方法来复制C#应用程序中的文件。 我发现,与Xcopy之类的东西相比,复制文件实际上相当慢

我提取了copy方法的核心功能,并将其放入一个测试控制台应用程序中,以获得它相对于Xcopy的运行速度,结果实际上完全不同

我得到的结果是:

异步方法:36.59秒-平均速度:1512.63 mb/秒

同步方法:36.49秒-平均速度:1516.72 mb/秒

XCOPY:5.62秒-平均速度:9842.11 mb/秒

所有这三个都使用了完全相同的文件和完全相同的目标

StreamExtensions类:

public static class StreamExtensions
    {

        const int DEFAULT_BUFFER = 0x1000; // 4096 bits

        public static async Task CopyToAsync(this Stream source, Stream destination, IProgress<long> progress, CancellationToken cancellationToken = default, int bufferSize = DEFAULT_BUFFER)
        {
            var buffer = new byte[bufferSize];
            int bytesRead;
            long totalRead = 0;

            while ((bytesRead = await source.ReadAsync(buffer, 0, buffer.Length, cancellationToken)) > 0)
            {
                await destination.WriteAsync(buffer, 0, bytesRead, cancellationToken);

                cancellationToken.ThrowIfCancellationRequested();

                totalRead += bytesRead;
                progress.Report(totalRead);
            }
        }

        public static void CopyTo(this Stream source, Stream destination, IProgress<long> progress, int bufferSize = DEFAULT_BUFFER)
        {
            var buffer = new byte[bufferSize];
            int bytesRead;
            long totalRead = 0;

            while ((bytesRead = source.Read(buffer, 0, buffer.Length)) > 0)
            {
                destination.Write(buffer, 0, bytesRead);

                totalRead += bytesRead;
                progress.Report(totalRead);
            }
        }
    }
公共静态类StreamExtensions
{
const int DEFAULT_BUFFER=0x1000;//4096位
公共静态异步任务CopyToAsync(此流源、流目标、IProgress进度、CancellationToken CancellationToken=default、int bufferSize=default\U BUFFER)
{
var buffer=新字节[bufferSize];
int字节读取;
长totalRead=0;
while((bytesRead=await source.ReadAsync(buffer,0,buffer.Length,cancellationToken))>0)
{
wait destination.WriteAsync(缓冲区,0,字节读取,取消令牌);
cancellationToken.ThrowIfCancellationRequested();
totalRead+=字节读取;
进度报告(totalRead);
}
}
公共静态void CopyTo(此流源、流目标、IProgress进程、int bufferSize=DEFAULT\u BUFFER)
{
var buffer=新字节[bufferSize];
int字节读取;
长totalRead=0;
而((bytesRead=source.Read(buffer,0,buffer.Length))>0)
{
写入(缓冲区,0,字节读取);
totalRead+=字节读取;
进度报告(totalRead);
}
}
}
IProgress
对象是向调用方法报告文件进度

调用实现示例:

// Asynchronous version
public static async Task CopyFileSetAsync(Dictionary<string, string> fileSet)
{
    for (var x = 0; x < fileSet.Count; x++)
    {
        var item = fileSet.ElementAt(x);
        var from = item.Key;
        var to = item.Value;

        int currentProgress = 0;

        long fileSize = new FileInfo(from).Length;

        IProgress<long> progress = new SynchronousProgress<long>(value =>
        {
            decimal fileProg = (decimal)(value * 100) / fileSize;

            if (fileProg != currentProgress)
            {
                currentProgress = (int)fileProg;
                OnUpdateFileProgress(null, new FileProgressEventArgs(fileProg));
            }
        });

        using (var outStream = new FileStream(to, FileMode.Create, FileAccess.Write, FileShare.Read))
        {
            using (var inStream = new FileStream(from, FileMode.Open, FileAccess.Read, FileShare.Read))
            {
                await inStream.CopyToAsync(outStream, progress);
            }
        }

        OnUpdateFileProgress(null, new FileProgressEventArgs(100)); // Probably redundant
    }
}

// Synchronous version
public static void CopyFileSet(Dictionary<string, string> fileSet)
{
    for (var x = 0; x < fileSet.Count; x++)
    {
        var item = fileSet.ElementAt(x);
        var from = item.Key;
        var to = item.Value;

        int currentProgress = 0;

        long fileSize = new FileInfo(from).Length;

        IProgress<long> progress = new SynchronousProgress<long>(value =>
        {
            decimal fileProg = (decimal)(value * 100) / fileSize;

            if (fileProg != currentProgress)
            {
                currentProgress = (int)fileProg;
                OnUpdateFileProgress(null, new FileProgressEventArgs(fileProg));
            }
        });

        using (var outStream = new FileStream(to, FileMode.Create, FileAccess.Write, FileShare.Read))
        {
            using (var inStream = new FileStream(from, FileMode.Open, FileAccess.Read, FileShare.Read))
            {
                inStream.CopyTo(outStream, progress, 1024);
            }
        }

        OnUpdateFileProgress(null, new FileProgressEventArgs(100)); // Probably redundant
    }
}
//异步版本
公共静态异步任务CopyFileSetSync(字典文件集)
{
对于(var x=0;x
{
decimal fileProg=(十进制)(值*100)/fileSize;
如果(fileProg!=当前进度)
{
currentProgress=(int)fileProg;
OnUpdateFileProgress(null,新FileProgressEventArgs(fileProg));
}
});
使用(var outStream=newfilestream(to,FileMode.Create,FileAccess.Write,FileShare.Read))
{
使用(var inStream=newfilestream(from,FileMode.Open,FileAccess.Read,FileShare.Read))
{
等待流内。复制同步(流外,进度);
}
}
OnUpdateFileProgress(null,新FileProgressEventArgs(100));//可能是冗余的
}
}
//同步版本
公共静态无效复制文件集(字典文件集)
{
对于(var x=0;x
{
decimal fileProg=(十进制)(值*100)/fileSize;
如果(fileProg!=当前进度)
{
currentProgress=(int)fileProg;
OnUpdateFileProgress(null,新FileProgressEventArgs(fileProg));
}
});
使用(var outStream=newfilestream(to,FileMode.Create,FileAccess.Write,FileShare.Read))
{
使用(var inStream=newfilestream(from,FileMode.Open,FileAccess.Read,FileShare.Read))
{
流内复制(流外,进度,1024);
}
}
OnUpdateFileProgress(null,新FileProgressEventArgs(100));//可能是冗余的
}
}
是否有什么东西阻止它以最快的速度运行?我只是不知道它比复制要慢多少

编辑:修复了一个打字错误,其中我忘记了一个'around IProgress

,多亏了,我回答了自己的问题:

我误解了缓冲区大小的影响。我只使用了8192字节作为缓冲区大小。在接受了他们的建议后,我将缓冲区大小增加到1mb(1048576字节),这对性能产生了巨大的影响

异步方法:5.57秒-平均速度:9938.68 mb/秒

同步方法:5.52秒-平均速度:10028.36 mb/秒


XCOPY:5.03秒-平均速度:11007.84 mb/秒

更改缓冲区大小是否会对所用时间产生明显影响?
DEFAULT\u buffer=0x1000;//4096位
。它们是字节,而不是位。
xcopy
使用与
robocopy
相同的底层代码,并且可能并行复制文件。正如Tom所建议的,尝试将缓冲区大小增加到1024*1024(1mb)像64K缓冲区这样的东西怎么样?你也曾尝试过官方的代码> FielestRAM.Copyto< /Cord>这可能避免了双拷贝吗?每个进程的峰值/平均内存/ CPU使用率是多少?你可以考虑移动缓冲器大小直到你看到一个有害的差异(我猜使用一个64字节的缓冲器可能快得多)。如果你能在85 K下得到它,然后你就远离这个大对象,我想你是说1MB而不是1MB。@jwdonahu