Skip to content

RoboCopy Benchmarking #192

@RFBomb

Description

@RFBomb

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions