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.
- The idea
- The race
- The memory plan
- Compact data
- Three languages
- Problems we found
- Right answers
- 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.
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
| Program | C processor | Zig | C 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.
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.
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.
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?
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
- Graphics card support. Work has started, with the same plan as the Go version.
- The close processor races. Lexer, raytrace, terrain and radix are the next targets.
Honest limits
- The project is an experiment. The work is not published.
- Earlier 2-thread tables gave C more threads than Zig, because a C flag did not limit its workers. Those tables are not on this page.
- We check the translation with tests. We do not have a mathematical proof that the Zig matches the Bend.
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.