Performance compared with Git

Command durations, storage sizes, and transfer sizes measured on one machine with hyperfine and the repository benchmark scripts.

Large files and media

Large, incompressible files and small edits. Content-defined chunking lets mkit reuse unchanged chunks when storing and transferring a new version.

Command duration

Mean wall-clock time over repeated runs of each CLI command, including process startup. Lower is better.

Commit a 1 MiB change to the 100 MiB file

mkit 19.1× faster

Append 1 MiB to the already-committed 100 MiB file, then add and commit the new version.

mkit
149ms
git
2.84s

Content-defined chunking re-hashes the file but stores only changed chunks, adding about 1 MiB. Git compresses and stores the full 101 MiB blob again. File rewrite costs depend on the storage system.

Add and commit one 1 GiB file

mkit 10.1× faster

Add and commit a 1 GiB file of incompressible bytes, with 3 runs per tool.

mkit
3.52s
git
35.7s

Time scales linearly with file size for both tools. mkit’s time goes to file I/O and BLAKE3 hashing. With only 3 runs, mkit’s standard deviation is large (1.04 s on a 3.52 s mean), but the ranges (2.7–4.7 s vs 35.5–36.0 s) do not overlap.

Add and commit one 100 MiB file

mkit 7.6× faster

The file holds incompressible bytes, standing in for video or other compressed media.

mkit
373ms
git
2.84s

mkit divides the file into roughly 1,300 chunks, hashes each with BLAKE3, and syncs writes to disk from a thread pool. Git’s SHA-1 hashing and zlib compression are CPU-bound. Run-to-run variation was lower in this measurement (a 43 ms standard deviation on a 373 ms mean), but shared disk I/O still affects these timings.

Checkout a branch that changed a 100 MiB file

about even

main has a committed 100 MiB file; branch v2 appends 1 MiB to it. Measures checking out v2 from main.

mkit
412ms
git
361ms

In this measurement git is about 14% faster, but mkit’s standard deviation is 166 ms on a 412 ms mean (runs ranged from 261 to 783 ms), so the two overlap and the data does not support a reliable winner. On 2026-09-19 a separate run (mkit commit d7afae5, same container class) measured mkit about 6% faster; the two runs disagree, which is consistent with noise. Restoring a ChunkedBlob writes every chunk to one shared file sequentially, and only each chunk’s read-and-verify step runs in parallel. cargo bench -p mkit-benches --bench restore_chunk_fanout measures that step alone: a 128 MiB restore’s chunk-read phase drops from 115.3 ms to 83.2 ms (about 28%). This row measures the full add, commit, checkout, and git process-spawn round trip end to end.

Storage size

Repository directory size (du -k .mkit vs .git) after the same operations. Lower is better.

One 100 MiB file, one commit

Repository size after the first commit of the 100 MiB file.

mkit
103.6 MiB
git
100.2 MiB

Git uses slightly less storage in this measurement. zlib does not reduce the incompressible content. mkit stores roughly 1,300 chunk objects, while Git stores one loose blob. Each file occupies whole 4 KiB filesystem blocks, so mkit’s chunk files add more overhead on ext4 than in the previous APFS measurement.

Growth after a 1 MiB change

Additional repository bytes after appending 1 MiB to the 100 MiB file and committing the second version.

mkit
1.2 MiB
git
101.1 MiB

mkit adds about 1.2 MiB: the appended data, one changed boundary chunk, and a new manifest. Git’s loose object store adds the full 101 MiB blob. After git gc, Git’s growth falls to about 1.0 MiB, similar to mkit.

Transfer size

Bytes a push sends after a small edit to a large file the remote already has. With delta encoding, mkit sends the changed chunk as a delta against the remote’s copy instead of the whole chunk. Lower is better.

Push a 16-byte edit to a 2 MiB file

47× smaller push

Edit 16 bytes in the middle of a 2 MiB FastCDC-chunked file the remote already holds, then push the new commit. Bytes counted on the wire.

whole
71.0 KiB
delta
1.5 KiB

The chunk delta is 93 bytes. The new manifest, tree, commit, and packmap node bring the transfer to about 1.5 KiB, compared with about 71 KiB for the changed chunk. Delta encoding applies only to transfers and only when it is smaller than the raw chunk. The receiver verifies each reconstructed object’s hash before storage.

Everyday operations

Common operations on small files and unchanged repositories. mkit signs every commit and flushes object writes to disk. In these benchmarks Git uses its defaults: unsigned commits and no fsync for loose objects.

Command duration

Mean wall-clock time over repeated runs of each CLI command, including process startup. Lower is better.

Add and commit 100 small files

mkit 2.4× faster

100 files of 10 KiB random bytes each, staged and committed together.

mkit
55.2ms
git
133ms

mkit makes each commit crash-durable by default with two full flushes before renaming objects into place, following Git’s core.fsyncMethod=batch design. Git does not fsync loose objects by default.

Re-add an unchanged 100 MiB file

mkit 2.4× faster

Run touch on the committed file to change its modification time without changing its contents, then run add to re-hash it.

mkit
109ms
git
260ms

The changed modification time invalidates both stat caches, so both tools read and hash the full 100 MiB again. Git’s SHA-1 pass takes longer than mkit’s BLAKE3 pass on this CPU.

Initialize an empty repository

mkit 1.2× faster

mkit init vs git init in a fresh directory.

mkit
3.3ms
git
3.8ms

Both commands finish in a few milliseconds, below hyperfine’s roughly 5 ms shell calibration threshold. mkit’s slowest run was about 7 ms across 332 runs. These timings do not support a reliable speed comparison.

Status with an unchanged 100 MiB file

git 1.2× faster

mkit status and git status in a clean repository containing the committed 100 MiB file, with a warm stat cache.

mkit
3.0ms
git
2.5ms

Both timings are below hyperfine’s roughly 5 ms shell calibration threshold. Both tools check cached file metadata with one stat call, without reading or hashing file contents.

Storage size

Repository directory size (du -k .mkit vs .git) after the same operations. Lower is better.

100 small files, one commit

Repository size after committing 100 × 10 KiB of random bytes (1,000 KiB of content).

mkit
1.6 MiB
git
1.7 MiB

Both repositories use similar storage: the content size plus per-object overhead.

Core microbenchmarks

Criterion benchmarks of a single mkit code path, comparing mkit against its own previous commit rather than Git. Median time; lower is better.

List 100 branch refs

23% faster

list_refs over 100 ref files on a warm page cache.

before
0.22ms
after
0.17ms

List 1,000 branch refs

15% faster

list_refs over 1,000 ref files on a warm page cache.

before
2.59ms
after
2.21ms

List 10,000 branch refs

23% faster

list_refs over 10,000 ref files on a warm page cache.

before
28.9ms
after
22.2ms

Measured 2026-09-28 with cargo bench -p mkit-benches --bench refs_ops on the same class of 4-core container as the methodology below. Each ref file is now read with one stack-buffer read (open, read, close) instead of fs::read, which adds a size-hint statx and an end-of-file probe read. About 23% faster at 10,000 refs and 25% at 100; the 1,000-ref run shows 15%.

Methodology and limitations

Date
Sep 28, 2026
Commit
a693e2eb
Machine
4-core Intel Xeon @ 2.10 GHz, 15 GB RAM, ext4 on a virtual disk, Linux 6.18 (Ubuntu 24.04). A shared virtualized container.
Versions
mkit 0.4.2 (release build, cargo build --release) · git 2.43.0 · hyperfine 1.20.0
Harness
scripts/bench-vs-git.sh — hyperfine with --warmup and per-command --prepare resetting a temp directory to a clean state between runs; 3 runs for the 1 GiB case, hyperfine defaults elsewhere; results from --export-json. Sizes via du -k.
Workload
Random (incompressible) bytes, representing already-compressed media such as video. Compressible content such as source code may compress better in Git’s zlib store; these benchmarks do not measure it.
  • Signing: every mkit commit is Ed25519-signed; Git commits are unsigned, which is Git’s default. Signing adds well under a millisecond per mkit commit, but the two sides do different work.
  • Durability: mkit batches each command’s object writes using two fixed full flushes plus per-file barriers (SPEC-OBJECTS §10.1), so a commit is durable when the command returns; Git does not fsync loose objects by default. To flush each object individually, set durability.objects = per-object.
  • Results are from one shared virtualized container. Scheduling and disk contention affect timings. Other hardware, operating systems, and filesystems may produce different ratios. Flush costs and small-file block-size overhead depend on the hardware and filesystem.
  • Both tools ran as CLI processes end to end, including process startup, with default configuration: no Git core.fsmonitor and no mkit tuning.