Re: Factor

Factor: the language, the theory, and the practice.

Getting Ziggy With It

Wednesday, March 18, 2026

#performance

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:

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:

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 library8% 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 suite13% 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!