-
Notifications
You must be signed in to change notification settings - Fork 0
Benchmarks and Comparison
dcon runs every container in its own Apple container microVM. That buys stronger isolation and a tiny idle footprint, but a fresh microVM is slower to start than a shared-VM engine. dcon is ~12× lighter at idle, ships a ~5.3 MB CLI instead of a multi-hundred-MB app bundle, and with the Warm Pool starts faster than OrbStack while keeping per-container isolation. Cold start and cold pull are bounded by Apple's runtime, not by dcon.
All numbers below are measured on the real machine — Apple silicon (Mac16,12), macOS 26, Apple container 1.0.0 — against the docker engine installed on the same host (OrbStack). Reproduce with make bench.
Medians of 5 runs, this host. dcon = Apple container; docker = OrbStack 29.4.0.
| metric | dcon (Apple container) | docker (OrbStack) |
|---|---|---|
| idle engine memory | ~91–92 MB | ~1004–1094 MB |
| CLI | ~5.3 MB single static binary | app bundle (100s of MB) |
| isolation model | per-container microVM (stronger) | shared Linux VM |
| background footprint | launchd helper, on-demand | persistent VM |
container start — warm pool (run --rm alpine echo) |
~90 ms, still per-container | ~206–212 ms (shared VM) |
| container start — cold (fresh microVM) | ~700–769 ms | ~206–212 ms |
cold pull alpine
|
~13 s¹ | ~3 s |
¹ network/registry/content-store bound (Apple's floor), not dcon overhead. See Limited by Apple below.
A few rows in plain English:
- Idle memory is ~12× lighter than OrbStack. Apple's services idle at ~91–92 MB, and microVMs exist only while a container is up. OrbStack keeps a full Linux VM resident at ~1 GB the whole time; Docker Desktop is heavier still at ~2000 MB. So vs Docker Desktop the memory gap is roughly ~22×, not 12×.
- Container start has two rows, on purpose. Cold (a brand-new microVM) is slower than a shared-VM engine, the cost of per-container isolation. The Warm Pool moves that boot off your critical path, and the resulting ~90 ms warm start beats OrbStack's ~212 ms by ~2.2× while every run still gets a pristine, never-reused VM. This is OrbStack's always-warm behavior on a per-container VM rather than a shared one.
- Cold pull is slower: ~13 s vs ~3 s. This is Apple's content-store and the network, not Go. Details below.
-
Host: Darwin arm64, Mac16,12, macOS 26. Apple
container1.0.0; OrbStack 29.4.0. -
Sampling: 5 runs per metric, median reported. Headline asset values are rounded (e.g. warm 90 / OrbStack 212 / cold 769 ms in
bench-startup.svg). - Images are pre-warmed before the start-latency runs, so the start numbers measure VM boot / exec, not a pull. The cold-pull row is measured separately on a cleared cache.
- Pull concurrency: runs use dcon's default of 8 concurrent layer downloads.
-
Reproduce:
make bench(drivesscripts/bench.sh, writesscripts/bench-results.md). Seescripts/bench-results.mdin the repo for the raw table from the latest run.
Numbers move with the host, the image, network conditions, and the Apple container version. Treat them as orders of magnitude, not guarantees, and rerun make bench on your own machine.
dcon is a thin translation layer (see Architecture). It is not where the time goes, and the profiling shows it.
dcon adds ~zero overhead. Raw container run --rm alpine echo measures ~700 ms; dcon run … measures ~690 ms, identical within noise. The entire cold-start cost is Apple's per-container microVM boot, not dcon's Go. You cannot optimize dcon to make a cold container start meaningfully faster, because dcon is already free.
Where the ~700 ms cold start actually goes:
| phase | cost | note |
|---|---|---|
container create |
~40 ms | cheap; just sets up the container record |
container start |
~650 ms | the boot: kernel + init + vminitd + network |
Pre-creating containers does not help: the cost lives in start, the boot itself. And Apple's apiserver serializes VM boots: launching 4 container run -d concurrently is only ~11% faster than running them one at a time. You cannot parallelize the boot path. That serialization is also why pure-boot Compose stacks gain only modestly (~11–16%) from concurrency, while build/pull-heavy stacks gain a lot more (those steps are network/builder-bound, not boot-bound).
Cold boot is Apple's floor, and the warm pool is the way past it. The Warm Pool pays the ~650 ms boot ahead of time, in the background, and then execs your workload into an already-running VM. An exec into a live VM is ~60–90 ms, and that floor is itself set by the container CLI's own Swift startup (~70 ms), not by dcon. There is no further headroom to win there either.
Cold pull (~13 s) is network + content-store bound, also Apple's floor. Two caveats so nobody over-claims:
- dcon raised the default layer-download concurrency from the backend's default of 3 to 8. That's the empirical knee; gains past 8 are under 5%. It's a real but modest knob, tunable via
--max-concurrent-downloadsorDCON_PULL_CONCURRENCY(clamped 1..32). - The large re-pull speedup you may notice is blob caching, not concurrency:
rmikeeps the layer blobs, so a subsequent pull reuses them. Do not read the cold-pull number as "concurrency is slow" or the re-pull number as "concurrency is fast"; they're different mechanisms.
| you care about | winner | why |
|---|---|---|
| idle memory on a laptop | dcon | ~92 MB vs ~1 GB (OrbStack) / ~2 GB (Docker Desktop) |
| install/footprint | dcon | ~5.3 MB static binary, no desktop app, no always-on daemon |
| per-container isolation | dcon | every run gets its own microVM (cold and warm) |
repeated short-lived --rm runs |
dcon (warm) | ~90 ms, ~2.2× faster than OrbStack |
| one-off cold container start | OrbStack | shared VM is already warm; dcon pays a fresh ~700 ms boot |
| first-time cold image pulls | OrbStack | ~3 s vs ~13 s; bounded by Apple's content store |
For mostly long-lived containers or warm, repeated runs, dcon is the lighter, better-isolated choice. For workloads dominated by one-shot cold boots or first-time pulls on a fresh cache, a shared-VM engine will feel snappier, and that gap is Apple's runtime, which dcon cannot erase.
- Warm Pool — how the ~90 ms path works, eligibility rules, and env knobs.
- Architecture — the translation pipeline and why dcon adds no overhead.
- Command Parity — full command surface, including compose parallelism.
-
scripts/bench-results.md— raw output of the latestmake bench. - README · cookbook · Home.