Why .NET async await file copy is a lot more CPU consuming than synchronous File.Copy() call?

These are pretty absurd perf numbers. You are simply not measuring what you think you are. This should not take more than a minor blip, a simple memory-to-memory copy for cached file data. Like File.Copy() did. Operates at ~35 gigabytes/second on a machine with decent DDR3 RAM so can't take more than a few dozen milliseconds. Even if the file is not cached or the machine doesn't have enough RAM then you still can't get this kind of CPU load, your code would be blocked waiting for the disk.

What you are actually seeing is the perf of your installed anti-malware product. It always gets its underwear in a bundle when it sees programs manipulating executable files.

Simple to verify, disable it or make an exclusion and try again.


File.OpenRead(sourceFileName) is equivalent to new FileStream(sourceFileName, FileMode.Open, FileAccess.Read, FileShare.Read) which is in turn equivalent to public FileStream(sourceFileName, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, false) which is to say with false for async I/O. The equivalent is true of the File.OpenWrite.

As such any XXXAsync operations won't use async I/O but will fake it using thread-pool threads.

So it gets none of the benefit of async I/O and wastes at least one thread. You've got an extra thread blocking on I/O which was what you wanted to avoid. I'd generally expect async on its own to perform slightly slower than sync (async generally sacrifices one-off speed for better scalability) but I'd definitely expect this to do little better, if at all, than wrapping the whole thing in Task.Run().

I'd still not expect it to be quite as bad, but maybe anti-malware is being worried by writing to an .exe.

You would hopefully fare better copying a non-exe and with asynchronous streams.


File.Copy appears to copy the entire file in one go. Using FileStreams the default buffer size is 4096 bytes so it will copy 4kb at a time.

I wrote my own async function which does more than just copy the file (it matches file sizes and does cleanup) but here are the results from bench-marking file copy over a VPN across a 50mbps broadband link.

When using the default 4096 bytes my async File Copy:

Copy of 52 files via CopyFileAsync() took 5.6 minutes

vs.

File.Copy which takes

Copy of 52 files via File.Copy() took 24 secs, 367 ms

When I increase the buffer size to 64KB I get the following

Copy of 52 files via CopyFileAsync() took 39 secs, 407 ms

Bottom line is the default buffer size of 4096 is way too small for modern hardware and that is why it is so slow copying via streams. You need to do bench marking against the hardware you will be using to determine the optimum buffer size but generally speaking 64K is fairly optimum for network traffic across the internet.