简体   繁体   English

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

[英]How can I improve the performance of this CopyTo method?

EDIT: I have now solved this.编辑:我现在已经解决了这个问题。 My answer posted below and will mark as solved when SO lets me.我的答案发布在下面,当 SO 允许我时将标记为已解决。

I have a CopyTo (and a CopyToAsync) method to copy files in my C# application.我有一个 CopyTo(和一个 CopyToAsync)方法来复制 C# 应用程序中的文件。 I have found that it is actually quite slow to copy the files, compared to something like Xcopy.我发现与 Xcopy 之类的东西相比,复制文件实际上很慢。

I extracted the core functionality of the copy method and placed it into a test console app to get the speed that it operates at versus Xcopy, and found the results actually quite different.我提取了 copy 方法的核心功能并将其放入测试控制台应用程序中,以获得它与 Xcopy 相比的运行速度,发现结果实际上完全不同。

The results I get are:我得到的结果是:

Async Method: 36.59 seconds - Average speed: 1512.63 mb/sec异步方法:36.59 秒 - 平均速度:1512.63 mb/sec

Sync Method: 36.49 seconds - Average speed: 1516.72 mb/sec同步方法:36.49 秒 - 平均速度:1516.72 mb/sec

XCOPY: 5.62 seconds - Average speed: 9842.11 mb/sec XCOPY:5.62 秒 - 平均速度:9842.11 mb/sec

All three of these used the exact same file, and the exact same destination.这三个都使用完全相同的文件和完全相同的目的地。

StreamExtensions class:流扩展 class:

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);
            }
        }
    }

The IProgress<long> object is to report the file progress back to the calling method. IProgress<long> object 是将文件进度报告回调用方法。

Example call implementation:示例调用实现:

// 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
    }
}

Is there something that's preventing this from running as fast as it could?有什么东西可以阻止它尽可能快地运行吗? I'm just stumped as to how much slower it is compared to copy.我只是对它与复制相比慢了多少感到困惑。

EDIT: Fixed a typo where I forgot a single ` around IProgress编辑:修正了一个错字,我在 IProgress 周围忘记了一个 `

Thanks to Tom and xanatos , I answered my own question:感谢Tomxanatos ,我回答了我自己的问题:

I misunderstood the impact of buffer size.我误解了缓冲区大小的影响。 I had only gone so far as 8192 bytes as the buffer size.我只达到了 8192 字节作为缓冲区大小。 After taking on their suggestions, I increased the buffer size to 1mb (1048576 bytes), and this made a massive difference to the performance.在接受了他们的建议后,我将缓冲区大小增加到 1mb(1048576 字节),这对性能产生了巨大的影响。

Async Method: 5.57 seconds - Average speed: 9938.68 mb/sec异步方法:5.57 秒 - 平均速度:9938.68 mb/sec

Sync Method: 5.52 seconds - Average speed: 10028.36 mb/sec同步方法:5.52 秒 - 平均速度:10028.36 mb/sec

XCOPY: 5.03 seconds - Average speed: 11007.84 mb/sec XCOPY:5.03 秒 - 平均速度:11007.84 mb/sec

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM