Performance
Zamin is designed for speed. This page covers the VM architecture, benchmark comparisons, optimization techniques, and practical tips for writing performant Zamin code.
Overview
Zamin's runtime is built around three key components:
- Bytecode Compilation - Source code is compiled to compact bytecode before execution. The compiler performs constant folding, dead code elimination, and peephole optimizations at compile time.
- Register-Based VM - The virtual machine executes bytecode using a register-based instruction set, reducing memory traffic compared to stack-based designs.
- Mark-and-Sweep Garbage Collector - A generational, non-moving GC with incremental collection phases minimizes pause times while keeping memory overhead low.
Benchmarks vs Python 3
Real-world benchmarks comparing Zamin and Python 3 on the same machine using time for wall-clock measurement. Lower is better.
| Benchmark | Zamin | Python 3 | Ratio |
|---|---|---|---|
| Recursive fib(32) | 1.51s | 0.35s | 4.3x |
| Integer loop (5M ops) | 0.47s | 0.53s | 0.9x |
| String concat (100K) | 3.14s | 0.23s | 13.4x |
| List push (500K) | 0.13s | 0.09s | 1.5x |
Zamin excels at tight integer loops — often matching or exceeding CPython — but is slower on string and list operations due to bytecode interpreter overhead.
Optimization Impact
The peephole optimizer and specialized integer opcodes provide significant speedups on core VM primitives:
| Optimization | Impact |
|---|---|
| Raw byte dispatch | 15-25% faster instruction dispatch vs function pointer table |
| Specialized integer opcodes | Avoids boxing and type-checks in hot integer loops |
| Constant folding | Eliminates runtime evaluation of constant expressions |
| Jump threading | Collapses chains of unconditional jumps |
| Dead code elimination | Removes unreachable branches after return/throw |
Optimization Techniques
Raw Byte Dispatch
The VM main loop uses a raw byte dispatch (computed gotos on GCC/Clang, switch-based fallback) instead of a function-pointer dispatch table. This eliminates an indirect call per instruction, giving a consistent 15-25% improvement across all workloads.
Specialized Integer Opcodes
Frequently used integer operations have dedicated opcodes that avoid the generic operand dispatch. For example, OP_ADD_INT_IMM adds a small constant to an integer register without boxing or type checking, and OP_LOAD_INT_CONST loads a compile-time constant directly into a register.
Peephole Optimizer
The bytecode compiler runs a peephole optimizer that performs:
- Constant folding - Compile-time evaluation of constant expressions
- Dead code elimination - Removal of unreachable branches and unused assignments
- Strength reduction - Replacing expensive operations with cheaper equivalents (e.g.,
x * 2becomesx << 1) - Jump chaining - Collapsing chains of unconditional jumps into a single target
- Opcode merging - Combining sequences like load-then-store into a single fused operation
Performance Tips
- Use f-strings for concatenation -
"Hello {name}"is faster than"Hello " + namebecause it avoids intermediate string allocations. - Pre-allocate lists - Use
list.new(capacity)when you know the final size to avoid repeated reallocations. - Prefer local variables - Local variable access is ~10x faster than global variable access in the VM.
- Use iterators over index loops - Iterator-based loops avoid bounds checking on each iteration.
- Avoid repeated allocation in loops - Move invariant computations outside the loop body. Create strings and lists once and reuse them.
- Use
zamin buildfor production - Building a standalone binary enables additional AOT optimizations that the interpreter cannot perform. - Profile before optimizing - Use
zamin run --disassembleto inspect bytecode and identify hotspots.