【发布时间】:2021-04-28 18:35:30
【问题描述】:
编辑:我现在已经解决了这个问题。我的答案贴在下面,当 SO 允许时将标记为已解决。
我有一个 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);
}
}
}
IProgress<long> 对象用于将文件进度报告回调用方法。
示例调用实现:
// 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
}
}
有什么东西阻止它尽可能快地运行吗?我只是对它与复制相比慢了多少感到困惑。
编辑:修正了一个错字,我在 IProgress 周围忘记了一个 `
【问题讨论】:
-
更改缓冲区大小对所用时间有明显影响吗?
-
DEFAULT_BUFFER = 0x1000; // 4096 bits。它们是字节,而不是位。 -
xcopy使用与robocopy相同的底层代码,并且可能并行复制文件。 -
按照 Tom 的建议,尝试将缓冲区大小增加到 1024 * 1024 (1mb)
-
64K 缓冲区怎么样?你也试过官方的
FileStream.CopyTo,因为这可能避免了双重复制?
标签: c# performance io