Experiment, updated 1 November 2026

Bend to Zig

This project translates programs from a new language, Bend, into Zig, a language with direct control of memory. The goal is the same answers at the same speed or better.

Pendingall-core race is running
16 of 16benchmarks give C's answer
635 of 640supported test programs pass on ARM
  1. The idea
  2. The race
  3. The memory plan
  4. Compact data
  5. Three languages
  6. Problems we found
  7. Right answers
  8. What comes next

The idea

Bend lets a programmer write down what a program must do, then have the computer prove that the program does it. That is very useful when an AI writes the code, because you do not have to trust the AI. Few teams use Bend today. This project gives those proofs to teams that use Zig.

Bend normally turns programs into C. It can also run them on a graphics card. So Zig has to compete with the best of both.

A processor is like a few expert chefs. A graphics card is like thousands of helpers who are fast only when they all do the same step together. Some jobs suit the chefs. Other jobs suit the helpers. The score below always compares with the faster of C on the processor and C on the graphics card.

Zig has no cleaner that stops the program to free memory, and it makes small data shapes easy to write. The translator cannot use the graphics card yet, so programs that suit the card are behind. That work is in progress.

The race, program by program

Each row is one test program. Further left is faster, and the scale is logarithmic because the times differ by up to 30 times. The ring shows the best C time. The filled dot shows Zig. Hover over a row to see all the times.

Run time on all 16 threads, Zig against best C

Zig and C on the processor ran in turn, with a 3-second rest. Each time is the best of 6 runs. C on the card comes from the Go table. Zig uses the processor only. Another job used up to 2 threads at low priority, and this table includes new work that the full test run has not checked.

See all the times as a table
ProgramC processorZigC card

The memory plan

With no cleaner, the program needs its own plan. Four parts work together.

1. A private tray for each worker

Each thread takes boxes from its own chunks of memory, with no lock.

2. Give boxes back

When nothing else can read a box, the program takes it back and uses it again.

3. Free a whole area

A sequential step can run in its own area of memory. When it ends, a few instructions free the whole area.

4. Let some go until later

The program does not walk a dropped tree. An area or the program's exit takes that memory.

Two threads build, sum and drop trees of 2 million nodes

Best time on a hand test. Lower is better.

Compact data is much faster

Zig's usual tagged type takes 16 bytes per value, so a node with two children takes 32. A hand-made word takes 8 bytes per value and 16 per node. That cuts memory traffic.

Tree programs: 8-byte word against the usual Zig type

Seconds at 2 threads. Each pair ran in turn, and the chart shows the best time. Lower is better.

Three languages, side by side

Each cell shows how a language compares with the best C time for that program. Blue is faster than C. Red is slower. A plain number means times faster, and a minus sign means times slower. Go uses the graphics card where the translator can. Zig and Odin use the processor only so far.

Speed against best C

Each lane uses the best C time from its own run. C on the graphics card comes from the Go run for Zig and Odin. Another job used up to 2 threads during these runs.

Problems we found

Assignment order

In Zig, one assignment to two names writes the first name before it computes the second value. This gave wrong answers on three programs and a hang on a fourth.

Shared values

Three kinds of shared values broke at first: match arms, array reads and fields of shared records. Each one gave wrong answers. Two of these reports led to fixes in the Go version too.

Names that look like types

In Zig, names like f2 and i3 are types. Generated names now take a suffix.

Ideas that did not help

Huge page alignment and freeing a node's first box last gave no gain. Unrolling a loop cut instructions by 15% but left cycles the same.

Does it give the right answers?

Test programs that pass

These runs came before the newest speed work.

The remaining failures are all about channel order, timing and thread count. For example, one test parks 20,000 senders, and each spawn here is a real system thread. Zig cannot fuse the float math it emits: the machine code for nbody and raytrace has no fused operations.

What comes next

Honest limits

Words used on this page
Processor (CPU)
The main chip in a computer. It has a few powerful workers.
Graphics card, or card (GPU)
A chip with thousands of simple workers.
Thread
One worker on the processor. This processor has 8 cores and 16 threads.
Cleaner (garbage collector)
The part of Go that finds and frees memory that a program no longer needs. Zig and Odin do not have one.
Box and node
A small piece of memory that holds part of a tree.
Cache miss
A read of data that is not near the processor. It can take 100 times longer than a read of nearby data.
Law check
A rule from the Bend program that the translated code must obey.