Getting Ziggy With It
Wednesday, March 18, 2026
It is possible that the next Factor release will be re-implemented in the Zig programming language and be faster in many cases compared to the current C++ VM.
As a reminder, we have had several implementation eras, using:
- the Java programming language until at least version 0.60,
- the C programming language from at least version 0.66 to version 0.91,
- the C++ programming language from version 0.92 to version 0.101.
Over the last few years, I’ve gained some experience using Zig. First,
learning some basics implementing SMAC in Factor and
then excitedly realizing that Factor is faster than
Zig. I later wrote the Zen of
Factor inspired partly by zig zen. And
recently, spent some time benchmarking Factor against Zig running One
Billion Loops.
It would be reasonable to ask if I’ve gotten Zig-pilled yet.
Maybe I’m now just getting ziggy with it.
Why Zig?
We are living in a golden age with many great programming languages and dedicated communities building excellent options to choose from across a variety of different dimensions. For example – and this is by no means an exhaustive or necessarily correct list – you could choose to prioritize:
- emphasizing memory safety and performance like Rust or OCaml
- established with newer cutting-edge features like C++ 26 or JDK 26
- extensive third-party libraries available like Python or TypeScript
- excellent platform interoperability like Swift
- robust concurrency stories like Erlang
- trying to “write fast, read fast, and run fast” like D language
- opinionated with great potential like Jai or Odin or Nim or Mojo
- based on popularity amongst developers
- and many, many, more…
There are some features that I particularly appreciate about Zig:
- a simple language with
- no hidden control flow,
- no hidden memory allocations,
- a great core development team,
- incremental and fast compilation,
- an emphasis on fast execution,
- builtin sanitizers and fuzzers, and
- nice error messages.
Porting the VM
Over the last few months, I have been working on a mostly apples-to-apples port of our existing C++ VM to Zig. It uses the same bootstrap process and is fully compatible with existing Factor image files.
Note: Since our aarch64 backend isn’t quite ready to run, testing was done
on an Ubuntu Linux 25.10 on x86_64. We hope for a future release to ship a
native aarch64 backend on macOS.
We are using a recent Zig nightly build:
$ zig version
0.16.0-dev.2915+065c6e794
The Listener – our REPL – works pretty great:
Factor 0.102 x86.64 (2305, heads/master-40edb95d40, Mar 18 2026 17:49:52)
[Zig 0.16.0-dev.2915+065c6e794 ReleaseFast] on linux
IN: scratchpad 1 2 + .
3
IN: scratchpad "hello" length .
5
IN: scratchpad 10 <iota> [ CHAR: a <string> ] map .
{
""
"a"
"aa"
"aaa"
"aaaa"
"aaaaa"
"aaaaaa"
"aaaaaaa"
"aaaaaaaa"
"aaaaaaaaa"
}
IN: scratchpad
Performance
At the risk of some distracting language wars, I wanted to particularly highlight some early performance results and other metrics comparing the two implementations: Zig vs C++.
Running the compiler tests – 20% faster:
! Zig VM
IN: scratchpad gc [ "compiler" test ] time
Running time: 9.692492884 seconds
! C++ VM
IN: scratchpad gc [ "compiler" test ] time
Running time: 12.658900299 seconds
Running the core tests – 22% faster:
# Zig VM
$ time ./zig-out/bin/factor -run=tools.test resource:core
real 1m8.776s
user 1m8.519s
sys 0m0.200s
# C++ VM
$ time ./factor -run=tools.test resource:core
real 1m29.935s
user 1m29.429s
sys 0m0.268s
Bootstrapping the Factor environment – 2% faster:
# Zig VM
$ ./zig-out/bin/factor -i=boot.unix-x86.64.image
Core bootstrap completed in 3 minutes and 22 seconds.
# C++ VM
$ ./factor -i=boot.unix-x86.64.image
Core bootstrap completed in 3 minutes and 27 seconds.
Running load-all with the standard library – 8% faster:
! Zig VM
IN: scratchpad gc [ load-all ] time
Running time: 522.531098916 seconds
! C++ VM
IN: scratchpad gc [ load-all ] time
Running time: 569.880413105 seconds
Running the benchmark suite – 13% faster:
! Zig VM
IN: scratchpad [ timing-benchmarks ] time
Running time: 438.671019723 seconds
! C++ VM
IN: scratchpad [ timing-benchmarks ] time
Running time: 508.235989841 seconds
And you can see that if we sort the benchmarks by percent improvements (worse
to better), some benchmarks that are bignum heavy are much faster, some
are a little faster, and a few have regressed:
Comparing lines of code – 67% more – with fewer files:
# Zig VM
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Language Files Lines Code Comments Blanks
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Zig 51 29034 21032 3761 4241
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Total 51 29034 21032 3761 4241
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# C++ VM
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Language Files Lines Code Comments Blanks
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Assembly 1 5 5 0 0
GNU Style Assembly 3 205 150 20 35
C 1 408 323 2 83
C Header 1 270 220 2 48
C++ 58 9911 7597 777 1537
C++ Header 84 5967 4297 606 1064
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Total 148 16776 12592 1407 2767
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Comparing binary sizes – 77% larger:
# Zig VM
-rwxrwxr-x 1 user staff 758K Mar 17 11:25 factor
# C++ VM
-rwxrwxr-x 1 user staff 430K Mar 8 13:39 factor
Next Steps
So, where does this go from here?
Well, besides making it also work on Windows, launch graphical programs properly, support compressed images, maybe support 32-bit, generally ensuring that it is a fully bug-free re-implementation of Factor, and investigating why the binaries are larger.
Perhaps it is an opportunity to re-think how the Factor bootstrap process works, to reduce the amount of functions a Factor VM should support, to run Factor in WASM with an optimizing compiler, to implement a simple Factor interpreter, or challenge our assumptions on what it means to be a Factor.
Or, it could just be a fun experiment using Zig. Let’s see!