diff --git a/Docker.DotNet.slnx b/Docker.DotNet.slnx
index dce2229d..2da18906 100644
--- a/Docker.DotNet.slnx
+++ b/Docker.DotNet.slnx
@@ -6,6 +6,8 @@
+
+
@@ -15,10 +17,9 @@
-
-
+
diff --git a/test/Docker.DotNet.Benchmarks/Docker.DotNet.Benchmarks.csproj b/test/Docker.DotNet.Benchmarks/Docker.DotNet.Benchmarks.csproj
new file mode 100644
index 00000000..3a14b63c
--- /dev/null
+++ b/test/Docker.DotNet.Benchmarks/Docker.DotNet.Benchmarks.csproj
@@ -0,0 +1,44 @@
+
+
+ net8.0;net9.0;net10.0
+ Exe
+ false
+ false
+
+
+ 3.132.0
+ false
+
+
+
+
+
+
+
+ $(DefineConstants);DOCKER_DOTNET_RELEASE
+
+
+
+
+
+
+
+ $(DefineConstants);DOCKER_DOTNET_MAIN
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/test/Docker.DotNet.Benchmarks/DockerDaemonRoundtripBenchmarks.cs b/test/Docker.DotNet.Benchmarks/DockerDaemonRoundtripBenchmarks.cs
new file mode 100644
index 00000000..75764279
--- /dev/null
+++ b/test/Docker.DotNet.Benchmarks/DockerDaemonRoundtripBenchmarks.cs
@@ -0,0 +1,112 @@
+namespace Docker.DotNet.Benchmarks;
+
+[MemoryDiagnoser]
+public class DockerDaemonRoundtripBenchmarks
+{
+ private readonly string _imageReference = "busybox:1.37";
+
+ private DockerClient _client = null!;
+
+ [GlobalSetup]
+ public async Task GlobalSetup()
+ {
+#if DOCKER_DOTNET_RELEASE
+ const string implementation = "release";
+#else
+ const string implementation = "main";
+#endif
+
+ var builder = new DockerClientBuilder();
+
+ _client = builder.Build();
+
+ Console.WriteLine($"Running daemon round-trip benchmarks against: {implementation}");
+
+ await _client.System.PingAsync(CancellationToken.None)
+ .ConfigureAwait(false);
+
+ await EnsureImageExistsAsync()
+ .ConfigureAwait(false);
+ }
+
+ [GlobalCleanup]
+ public void GlobalCleanup()
+ {
+ _client.Dispose();
+ }
+
+ [Benchmark]
+ public async Task CreateContainerRequestResponse()
+ {
+ var parameters = new CreateContainerParameters();
+ parameters.Name = $"dockerdotnet-benchmark-{Guid.NewGuid():N}";
+ parameters.Image = _imageReference;
+ parameters.Cmd = ["sh", "-c", "sleep 1"];
+ parameters.Labels = new Dictionary();
+ parameters.Labels.Add("suite", "benchmark");
+ parameters.Labels.Add("scenario", "create-roundtrip");
+
+ var response = await _client.Containers.CreateContainerAsync(parameters, CancellationToken.None)
+ .ConfigureAwait(false);
+
+ try
+ {
+ return response.ID.Length;
+ }
+ finally
+ {
+ await SafeRemoveContainerAsync(response.ID)
+ .ConfigureAwait(false);
+ }
+ }
+
+ [Benchmark]
+ public async Task StartContainerRequestResponse()
+ {
+ var parameters = new CreateContainerParameters();
+ parameters.Name = $"dockerdotnet-benchmark-{Guid.NewGuid():N}";
+ parameters.Image = _imageReference;
+ parameters.Cmd = ["sh", "-c", "exit 0"];
+ parameters.Labels = new Dictionary();
+ parameters.Labels.Add("suite", "benchmark");
+ parameters.Labels.Add("scenario", "start-roundtrip");
+
+ var response = await _client.Containers.CreateContainerAsync(parameters, CancellationToken.None)
+ .ConfigureAwait(false);
+
+ try
+ {
+ return await _client.Containers.StartContainerAsync(response.ID, new ContainerStartParameters(), CancellationToken.None)
+ .ConfigureAwait(false);
+ }
+ finally
+ {
+ await SafeRemoveContainerAsync(response.ID)
+ .ConfigureAwait(false);
+ }
+ }
+
+ private async Task EnsureImageExistsAsync()
+ {
+ var parameters = new ImagesCreateParameters();
+ parameters.FromImage = _imageReference;
+
+ await _client.Images.CreateImageAsync(parameters, new AuthConfig(), new Progress(_ => { }), CancellationToken.None)
+ .ConfigureAwait(false);
+ }
+
+ private async Task SafeRemoveContainerAsync(string containerId)
+ {
+ var parameters = new ContainerRemoveParameters();
+ parameters.Force = true;
+
+ try
+ {
+ await _client.Containers.RemoveContainerAsync(containerId, parameters, CancellationToken.None)
+ .ConfigureAwait(false);
+ }
+ catch (DockerContainerNotFoundException)
+ {
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/Docker.DotNet.Benchmarks/Program.cs b/test/Docker.DotNet.Benchmarks/Program.cs
new file mode 100644
index 00000000..10434bbc
--- /dev/null
+++ b/test/Docker.DotNet.Benchmarks/Program.cs
@@ -0,0 +1,2 @@
+Type[] benchmarks = [typeof(DockerDaemonRoundtripBenchmarks)];
+BenchmarkSwitcher.FromTypes(benchmarks).Run(args);
\ No newline at end of file
diff --git a/test/Docker.DotNet.Benchmarks/README.md b/test/Docker.DotNet.Benchmarks/README.md
new file mode 100644
index 00000000..180bb425
--- /dev/null
+++ b/test/Docker.DotNet.Benchmarks/README.md
@@ -0,0 +1,23 @@
+# Docker.DotNet Benchmarks
+
+This benchmark project compares the current `main` source code against a released NuGet package by running the same benchmark class twice.
+
+## Run
+
+From the repository root, run `main` first:
+
+```bash
+dotnet run -c Release --project test/Docker.DotNet.Benchmarks/Docker.DotNet.Benchmarks.csproj -- --filter '*DockerDaemonRoundtripBenchmarks*'
+```
+
+Then run the released NuGet package implementation:
+
+```bash
+dotnet run -c Release --project test/Docker.DotNet.Benchmarks/Docker.DotNet.Benchmarks.csproj -p:UseReleasedPackage=true -- --filter '*DockerDaemonRoundtripBenchmarks*'
+```
+
+To compare against a different release tag/package version:
+
+```bash
+dotnet run -c Release --project test/Docker.DotNet.Benchmarks/Docker.DotNet.Benchmarks.csproj -p:UseReleasedPackage=true -p:DockerDotNetReleaseVersion=3.132.0 -- --filter '*DockerDaemonRoundtripBenchmarks*'
+```