Optimizing the Zef Interpreter: From Scratch to Competitive Performance
AI Summary
I embarked on a journey to optimize an AST-walking interpreter for Zef, a dynamic language I created for fun, to compete with established interpreters like Lua, QuickJS, and CPython. Unlike typical optimization stories focusing on JIT compilers or garbage collectors, my approach started from a basic foundation, achieving a 16x speed-up, and even 67x with an incomplete Yolo-C++ port.
## Initial Setup
The original Zef interpreter was built without performance considerations, relying on a 64-bit tagged value representation and C++. This setup allowed for quick development but resulted in significant performance drawbacks, such as being 35x slower than CPython. The interpreter was heavily reliant on recursive AST walking and used strings and hashtables extensively, leading to inefficiencies.
## Optimization Techniques
- **Direct Operators**: By generating distinct AST nodes for each operator, I avoided string lookups, achieving a 17.5% speed-up.
- **Direct RMWs**: Similar to operators, read-modify-write operations were optimized by generating specific nodes, resulting in a 3.7% speed-up.
- **Avoid IntObject Checks**: Simplifying integer checks by handling IntObjects separately improved performance by 1%.
- **Symbols**: Replacing strings with hash-consed Symbol objects for lookups led to an 18% improvement.
- **Value Inline**: Inlining critical functions provided a 2.8% speed-up.
- **Object Model and Inline Caches**: A major overhaul of the object model, combined with inline caches and watchpoints, resulted in a 4.55x speed-up.
- **Arguments Optimization**: Introducing a specialized Arguments type reduced allocations, enhancing speed by 1.33x.
- **Getter and Setter Specialization**: By inferring and specializing getters and setters, I achieved additional speed-ups of 5.6% and 3.4%, respectively.
- **callMethod Inlining and Hashtable Optimization**: These changes further reduced method call overhead, with a combined speed-up of over 18%.
## Final Steps and Yolo-C++
The final optimizations included avoiding std::optional, specializing common operations like sqrt and toString, and improving C++ configurations. Turning off assertions and experimenting with Yolo-C++ provided additional insights, though the latter was unsound due to memory management issues.
By the end of these optimizations, Zef was 16.6x faster than the original version, and with Yolo-C++, it was 67x faster, outperforming CPython and QuickJS in some benchmarks.
Key Concepts
An interpreter that processes an Abstract Syntax Tree (AST) directly to execute code. It traverses the tree structure, evaluating nodes to perform computations.
A technique used in dynamic language implementations to speed up method calls by caching the results of previous lookups, reducing the need for repeated expensive operations.
Category
ProgrammingOriginal source
https://zef-lang.dev/implementationMore on Discover
Summarized by Mente
Save any article, video, or tweet. AI summarizes it, finds connections, and creates your to-do list.
Start free, no credit card