为什么 .NET async await 文件复制 CPU 比同步 File.Copy() 调用消耗更多?
Why .NET async await file copy is a lot more CPU consuming than synchronous File.Copy() call?
为什么下面的代码会导致:
public static class Program
{
public static void Main(params string[] args)
{
var sourceFileName = @"C:\Users\ehoua\Desktop\Stuff0MFile.exe";
var destinationFileName = sourceFileName + ".bak";
FileCopyAsync(sourceFileName, destinationFileName);
// The line below is actually faster and a lot less CPU-consuming
// File.Copy(sourceFileName, destinationFileName, true);
Console.ReadKey();
}
public static async void FileCopyAsync(string sourceFileName, string destinationFileName, int bufferSize = 0x1000, CancellationToken cancellationToken = default(CancellationToken))
{
using (var sourceFile = File.OpenRead(sourceFileName))
{
using (var destinationFile = File.OpenWrite(destinationFileName))
{
Console.WriteLine($"Copying {sourceFileName} to {destinationFileName}...");
await sourceFile.CopyToAsync(destinationFile, bufferSize, cancellationToken);
Console.WriteLine("Done");
}
}
}
}
虽然 File.Copy(): https://msdn.microsoft.com/en-us/library/system.io.file.copy(v=vs.110).aspx 少了很多 cpu-消耗:
那么使用 async / await 进行文件复制是否真的有兴趣?
我认为为复制保存线程可能值得,但 File.Copy windows 函数似乎在 CPU % 方面赢得了胜利。有些人会争辩说这是因为真正的 DMA 支持,但我是否正在做任何破坏性能的事情?或者有什么可以改进我的异步方法的 CPU 用法吗?
这些是非常荒谬的性能数字。你根本没有衡量你认为自己是什么。这应该不会超过一个小问题,一个缓存文件数据的简单内存到内存副本。就像 File.Copy() 那样。在具有良好 DDR3 RAM 的机器上以 ~35 gigabytes/second 运行,因此不会超过几十毫秒。即使文件未缓存或机器没有足够的 RAM,您仍然无法获得这种 CPU 负载,您的代码将被阻塞等待磁盘。
您实际上看到的是您安装的反恶意软件产品的性能。当它看到程序操纵可执行文件时,它总是把它的内衣捆绑在一起。
验证简单,禁用或排除再试。
File.OpenRead(sourceFileName)
等同于 new FileStream(sourceFileName, FileMode.Open, FileAccess.Read, FileShare.Read)
,后者又等同于 public FileStream(sourceFileName, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, false)
,也就是说 false
用于异步 I/O。等价于 File.OpenWrite
.
因此任何 XXXAsync
操作都不会使用异步 I/O 但会使用线程池线程来伪造它。
因此它获得了 none 异步 I/O 的好处并且至少浪费了一个线程。您在 I/O 上有一个额外的线程阻塞,这是您想避免的。我通常希望异步本身的执行速度比同步稍慢(异步通常会牺牲一次性速度以获得更好的可伸缩性)但我绝对希望这样做比将整个事情包装在 Task.Run()
.
我仍然不认为它会那么糟糕,但也许反恶意软件正在担心写入 .exe。
您希望复制非 exe 文件并使用异步流会更好。
File.Copy 似乎是一次性复制了整个文件。
使用 FileStreams,默认缓冲区大小为 4096 字节,因此它将一次复制 4kb。
我编写了自己的 async
函数,它不仅可以复制文件(它匹配文件大小并进行清理),而且这里是 VPN
基准文件复制的结果通过 50mbps 宽带 link.
当使用默认的 4096 字节时,我的 async
文件复制:
Copy of 52 files via CopyFileAsync() took 5.6 minutes
对比
File.Copy需要
Copy of 52 files via File.Copy() took 24 secs, 367 ms
当我将缓冲区大小增加到 64KB 时,我得到以下结果
Copy of 52 files via CopyFileAsync() took 39 secs, 407 ms
底线是 4096 的默认缓冲区大小对于现代硬件来说太小了,这就是它通过流复制如此缓慢的原因。您需要对您将使用的硬件进行基准测试以确定最佳缓冲区大小,但一般来说 64K 对于 Internet 上的网络流量来说是相当最佳的。
为什么下面的代码会导致:
public static class Program
{
public static void Main(params string[] args)
{
var sourceFileName = @"C:\Users\ehoua\Desktop\Stuff0MFile.exe";
var destinationFileName = sourceFileName + ".bak";
FileCopyAsync(sourceFileName, destinationFileName);
// The line below is actually faster and a lot less CPU-consuming
// File.Copy(sourceFileName, destinationFileName, true);
Console.ReadKey();
}
public static async void FileCopyAsync(string sourceFileName, string destinationFileName, int bufferSize = 0x1000, CancellationToken cancellationToken = default(CancellationToken))
{
using (var sourceFile = File.OpenRead(sourceFileName))
{
using (var destinationFile = File.OpenWrite(destinationFileName))
{
Console.WriteLine($"Copying {sourceFileName} to {destinationFileName}...");
await sourceFile.CopyToAsync(destinationFile, bufferSize, cancellationToken);
Console.WriteLine("Done");
}
}
}
}
虽然 File.Copy(): https://msdn.microsoft.com/en-us/library/system.io.file.copy(v=vs.110).aspx 少了很多 cpu-消耗:
那么使用 async / await 进行文件复制是否真的有兴趣?
我认为为复制保存线程可能值得,但 File.Copy windows 函数似乎在 CPU % 方面赢得了胜利。有些人会争辩说这是因为真正的 DMA 支持,但我是否正在做任何破坏性能的事情?或者有什么可以改进我的异步方法的 CPU 用法吗?
这些是非常荒谬的性能数字。你根本没有衡量你认为自己是什么。这应该不会超过一个小问题,一个缓存文件数据的简单内存到内存副本。就像 File.Copy() 那样。在具有良好 DDR3 RAM 的机器上以 ~35 gigabytes/second 运行,因此不会超过几十毫秒。即使文件未缓存或机器没有足够的 RAM,您仍然无法获得这种 CPU 负载,您的代码将被阻塞等待磁盘。
您实际上看到的是您安装的反恶意软件产品的性能。当它看到程序操纵可执行文件时,它总是把它的内衣捆绑在一起。
验证简单,禁用或排除再试。
File.OpenRead(sourceFileName)
等同于 new FileStream(sourceFileName, FileMode.Open, FileAccess.Read, FileShare.Read)
,后者又等同于 public FileStream(sourceFileName, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, false)
,也就是说 false
用于异步 I/O。等价于 File.OpenWrite
.
因此任何 XXXAsync
操作都不会使用异步 I/O 但会使用线程池线程来伪造它。
因此它获得了 none 异步 I/O 的好处并且至少浪费了一个线程。您在 I/O 上有一个额外的线程阻塞,这是您想避免的。我通常希望异步本身的执行速度比同步稍慢(异步通常会牺牲一次性速度以获得更好的可伸缩性)但我绝对希望这样做比将整个事情包装在 Task.Run()
.
我仍然不认为它会那么糟糕,但也许反恶意软件正在担心写入 .exe。
您希望复制非 exe 文件并使用异步流会更好。
File.Copy 似乎是一次性复制了整个文件。 使用 FileStreams,默认缓冲区大小为 4096 字节,因此它将一次复制 4kb。
我编写了自己的 async
函数,它不仅可以复制文件(它匹配文件大小并进行清理),而且这里是 VPN
基准文件复制的结果通过 50mbps 宽带 link.
当使用默认的 4096 字节时,我的 async
文件复制:
Copy of 52 files via CopyFileAsync() took 5.6 minutes
对比
File.Copy需要
Copy of 52 files via File.Copy() took 24 secs, 367 ms
当我将缓冲区大小增加到 64KB 时,我得到以下结果
Copy of 52 files via CopyFileAsync() took 39 secs, 407 ms
底线是 4096 的默认缓冲区大小对于现代硬件来说太小了,这就是它通过流复制如此缓慢的原因。您需要对您将使用的硬件进行基准测试以确定最佳缓冲区大小,但一般来说 64K 对于 Internet 上的网络流量来说是相当最佳的。