# Benchmarks (/docs/benchmarks)

All numbers compare marmot with `go build` (Go 1.27) on the same machine. A program counts
only if its output is identical to Go's. Each result is the median of five runs after one
warm-up run.

<Callout title="Early numbers">
These are early numbers from a young compiler. They will change.
</Callout>

## Run time [#run-time]

Apple M3 Pro, 43 programs.

| Build | Average speed against Go |
| --- | --- |
| `marmot build` (dev, fast code generator) | not measured yet |
| `marmot build` (dev, `MARMOT_TIER0=0`) | 1.4x faster |
| `marmot build -release` | 1.6x faster |

"Average" is the geometric mean. 33 of the 43 programs are faster than Go in
release mode and 10 are slower. An 8-core Intel desktop running Linux gives a similar picture:
1.6x faster on average over 45 programs.

### By kind of program [#by-kind-of-program]

<Diagram name="speed" alt="Bar chart of speed against Go in release mode: convolution 17.8x, bit tricks 10.1x, sum min max 8.9x, struct copy 4.6x, interface calls 3.5x, string search 2.8x, closures 2.0x, channel ping-pong 1.5x, quicksort 1.0x, tree building 0.9x, defer in a loop 0.7x" caption="Times faster than Go, release mode. 1 is the same as Go." />

| Program | What it stresses | Against Go |
| --- | --- | --- |
| convolution | numeric loops | 17.8x faster |
| bit tricks | integer operations | 10.1x faster |
| sum, min, max | simple loops over slices | 8.9x faster |
| struct copy | copying values | 4.6x faster |
| interface calls | method calls through interfaces | 3.5x faster |
| string search | finding substrings | 2.8x faster |
| closures | calling function values | 2.0x faster |
| channel ping-pong | goroutine hand-off | 1.5x faster |
| quicksort | branches and swaps | about equal |
| tree building | allocation and garbage collection | 0.9x (slower) |
| defer in a loop | `defer` cost | 0.7x (slower) |

Where marmot wins big, the cause is usually vector instructions that Go's compiler does not
emit. Where it loses, the cause is allocation and `defer`, which Go has tuned for years.

### Loops that can run in parallel [#loops-that-can-run-in-parallel]

marmot can prove that the iterations of some loops do not depend on each other, and then run
them on all cores. This is off by default while it is being reviewed. With it on:

<Diagram name="parallel" alt="Bar chart of speed against Go with parallel loops: matrix multiply 36x, mandelbrot 17x, spectral norm 17x, matrix transpose 12x" caption="Times faster than Go, with parallel loops on. Wall clock time, using every core." />

These use every core, so they are faster in wall-clock time, not in total CPU work.

## Binary size [#binary-size]

<Diagram name="size" alt="Bar chart of binary size in KB, marmot against go, for each program in the table below" caption="Binary size in KB. The first bar of each pair is marmot, the second go." />

<table>
  <thead><tr><th>Program</th><th>marmot</th><th>go</th></tr></thead>
  <tbody>hello world with println, -release148 KB1.68 MBhello world with fmt.Println, -release665 KB2.4 MBhello world with println, -O z100 KB1.17 MB (stripped)small program using fmt, sort, strings, a map500 KB1.62 MB (stripped)</tbody>
</table>

## Build time [#build-time]

### First build [#first-build]

Building the compiler itself (`./cmd/marmot`, 238 packages including the standard library)
from empty caches on both sides, measured 2026-11-13 10:46 on a quiet Apple M3 Pro:

<table>
  <thead><tr><th>Case</th><th>marmot</th><th>go</th></tr></thead>
  <tbody><tr><td>first build of cmd/marmot, empty caches</td><td>5.13 to 5.43 s</td><td>5.23 to 6.34 s</td></tr></tbody>
</table>

A first build compiles every package of the standard library the program uses.
Rebuilds are where the build cache and the
incremental front end pay off.

### Rebuilds [#rebuilds]

The same tree and the same run (2026-11-13 10:46, 238 packages), with the incremental front end, the
default. The edits add one line to a package: "leaf" is the main package, "deep" is a package
that most of the compiler uses. Each case ran twice for each compiler, alternating.

<Diagram name="buildtime" alt="Bar chart of rebuild time in seconds, marmot against go, for the cases in the table below" caption="Rebuild time in seconds, the upper end of each range. The first bar of each pair is marmot, the second go." />

<table>
  <thead><tr><th>Case</th><th>marmot</th><th>go</th></tr></thead>
  <tbody>rebuild, nothing changed0.05 to 0.07 s0.14 to 0.15 sone line edit, leaf package0.68 to 0.70 s0.50 to 0.51 sone line edit, deep package0.94 to 0.96 s0.90 to 0.93 s</tbody>
</table>

A rebuild with nothing changed is where the build cache pays off.

Dev builds use a fast code generator that writes machine code directly, without LLVM
(`MARMOT_TIER0=0` turns it off). It is never used with `-release`.

## Memory [#memory]

On small programs marmot binaries use about half the memory of Go binaries at run time, mostly
because the runtime starts fewer things up front.

## How to reproduce [#how-to-reproduce]

```sh
make
go run ./bench/cmd/bench -list        # see the programs
go run ./bench/cmd/bench              # run them
go run ./bench/report                 # write the report
```

Run benchmarks on an idle machine. Other work on the same machine changes the results a lot.