-
Notifications
You must be signed in to change notification settings - Fork 69
Description
I'm currently writing a custom IRoboCommand to orchestrate building a complex file batch that pulls from various folders and singular files to a final destination. As such, I've written an interface that has a method for CopyToAsync() to copy a file from source to destination asynchronously. I'm creating this thread to document some benchmarks, as RoboCopy is great at what it does.
I used ChatGPT and the microsoft documentation to create some async methods to handle the copying that reports via an IProgress<long> object. As well as a project developed to use CopyFileEx to perform the copy operation.
The benchmark for these tests was run in a console app in release mode, using a 334MB file. Each copy operation was performed 10x and the time report was the average result. My goal was to determine if a comparable time-to-copy could be achieve for a single file that falls within the current documentation recommendations I've been able to track down over a few days.
Note : This test was done on a console application targeting Net5.0 (as my work computer does not have VS2022 yet). Net6.0 benchmarks will happen in a followup comment.
CopyFileEx : https://github.com/RFBCodeWorks/CachedRoboCopy/blob/master/CachedRoboCopy/FileCopier.cs
static async Task NoProgressAsync(string source, string destination, int bufferSize = 81920)
{
using (var reader = File.OpenRead(source))
{
using (var writer = File.OpenWrite(destination))
{
await reader.CopyToAsync(writer, bufferSize);
writer.Dispose();
reader.Dispose();
}
}
}
public static async Task ProgressAsync(string source, string destination, IProgress<long> progress, bool overwrite = false, CancellationToken token = default, int bufferSize = 81920)
{
token.ThrowIfCancellationRequested();
if (!File.Exists(source)) throw new FileNotFoundException("File not found : " + source);
if (!overwrite && File.Exists(destination)) throw new IOException("Destination file already exists");
if (progress is null) throw new ArgumentNullException(nameof(progress));
int reportingInterval = 250;
Stopwatch reportTimer = new Stopwatch();
try
{
using (FileStream sourceStream = new FileStream(source, FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize, true))
using (FileStream destinationStream = new FileStream(destination, FileMode.Create, FileAccess.Write, FileShare.None, bufferSize, true))
{
byte[] buffer = new byte[bufferSize];
long totalBytesRead = 0;
int bytesRead;
reportTimer.Start();
while ((bytesRead = await sourceStream.ReadAsync(buffer, 0, buffer.Length, token).ConfigureAwait(false)) > 0)
{
token.ThrowIfCancellationRequested();
await destinationStream.WriteAsync(buffer, 0, bytesRead, token).ConfigureAwait(false);
totalBytesRead += bytesRead;
if (reportTimer.ElapsedMilliseconds >= reportingInterval)
{
progress?.Report(totalBytesRead);
reportTimer.Restart();
}
}
reportTimer.Stop();
progress?.Report(totalBytesRead);
}
}
catch (OperationCanceledException)
{
// If the operation was canceled, delete the destination file
if (File.Exists(destination))
{
File.Delete(destination);
Console.WriteLine($"File copy operation canceled. Destination file '{destination}' deleted.");
}
throw; // Re-throw the OperationCanceledException after handling
}
}