Skip to content

Container with process isolation is ~2.5x slower compared to host on CPU-bound loads #636

@athierry-oct

Description

@athierry-oct

Describe the bug

I ran a simple CPU benchmark (cpu_benchmark.ps1, attached at the end) and the results surprised me a bit:

Target Average run time
Host 1,812.615 ms
Container (process isolation) 4,252.186 ms
Container (hyperv isolation) 1,804.605 ms

Hyper-V isolation yields about the same performance as the host, but process isolation is much slower.

To Reproduce

Run the following:

docker run -it --rm --isolation=process -v [folder containing the benchmark script]:C:\benchmark mcr.microsoft.com/windows/servercore:ltsc2025

C:\>powershell

PS C:\> C:\benchmark\cpu_benchmark.ps1

The output will look like this:

PS C:\> C:\benchmark\cpu_benchmark.ps1
CPU benchmark starting...
Iterations : 20000000
Runs       : 6 (first run discarded as warm-up)

Run 1: 4,386.772 ms (warm-up, discarded)
Run 2: 4,297.680 ms
Run 3: 4,427.794 ms
Run 4: 4,435.934 ms
Run 5: 4,387.221 ms
Run 6: 4,458.746 ms

Expected behavior

I would have expected similar performance on process isolation, compared to the host and hyperv isolation.

Configuration:

  • Edition: Windows 11 (version 25H2 build 26200.8246)
  • Base Image being used: Windows servercore ltsc2025
  • Container engine: Docker Desktop 4.68.0
  • Container Engine version: 29.3.1

Additional context

I investigated the issue with ETW (see attached traces at the end), and noticed a big difference regarding context switches.

  • This is the ETW trace on the host, showing the CPU usage and context switches (The high CPU activity in the CPU usage (sampled) view corresponds to the time the benchmark runs):
Image
  • And this is the ETW trace on the container with process isolation:
Image

On the container, there seem to be much more frequent and regular context switches for the powershell process. In addition, it spends much more time in the WAIT and READY states.

I'm not a ETW expert, but this suggests that on the container, the benchmark is preempted much more frequently than on the host, which could explain the drop in performance.

Is this a known issue?
Is there a workaround?

Thanks in advance!


ETW traces:
2026-04-15_16-50-18 Container performance investigation (host).zip

2026-04-15_16-56-15 Container performance investigation (container with process isolation).zip

cpu_benchmark.ps1

param (
    [long]$Iterations = 20000000,
    [int]$Runs = 6
)

if ($Runs -lt 2) {
    throw "Runs must be at least 2 so the first run can be discarded as warm-up."
}

function Invoke-BenchmarkRun {
    param (
        [long]$RunIterations
    )

    $sw = [System.Diagnostics.Stopwatch]::StartNew()

    [long]$x = 0
    for ([long]$i = 0; $i -lt $RunIterations; $i++) {
        $x += $i
    }

    $sw.Stop()
    return $sw.Elapsed.TotalMilliseconds
}

Write-Host "CPU benchmark starting..."
Write-Host "Iterations : $Iterations"
Write-Host "Runs       : $Runs (first run discarded as warm-up)"
Write-Host ""

$measurements = New-Object System.Collections.Generic.List[double]

for ($run = 1; $run -le $Runs; $run++) {
    $elapsedMs = Invoke-BenchmarkRun -RunIterations $Iterations

    if ($run -eq 1) {
        Write-Host ("Run {0}: {1:N3} ms (warm-up, discarded)" -f $run, $elapsedMs)
    }
    else {
        $measurements.Add($elapsedMs)
        Write-Host ("Run {0}: {1:N3} ms" -f $run, $elapsedMs)
    }
}

$averageMs = ($measurements | Measure-Object -Average).Average
$averageTime = [TimeSpan]::FromMilliseconds($averageMs)

Write-Host ""
Write-Host ("Average time: {0} ({1:N3} ms)" -f $averageTime, $averageMs)

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingtriageNew and needs attention

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions