BlogsShopifyYJIT Compiler for Ruby

YJIT Compiler for Ruby

YJIT Compiler for Ruby

18
posts
2015–2023

Shopify engineers worked on many cool and impactful projects in 2021. Exhibit A: YJIT. Created by a small team led by Staff Engineer Maxime Chevalier-Boisvert, YJIT is a new JIT (just-in-time) compiler built inside CRuby that has now been merged upstream and is part of the Ruby 3.1.0 release. In this post, Maxime writes about the importance of the project to Shopify and Ruby developers worldwide. This post details the process of contributing support for new WebAssembly instructions to Winch, Was. This year, YJIT gained the ability to generate machine code for ARM platforms, enabling its use on ARM servers and Apple Silicon development machines. The YJIT compiler was also rewritten in Rust for improved maintainability and performance.

2023

Contributing support for a Wasm instruction to Winch - Shopify

7/18/2023

This post details the technical process of contributing support for new WebAssembly instructions to Winch, Wasmtime's baseline compiler. It explains the steps involved: modifying the visitor to handle new instructions, implementing methods in the MacroAssembler and CodeGenContext for instruction emission and operand management, emitting x86_64 machine instructions via the assembler, and testing through filetests and differential fuzzing. Specific examples of adding support for `i32.clz` and `i32.and` instructions are provided, along with explanations of how to use Cranelift's ISLE definitions and the challenges of testing on different architectures.

Ruby 3.2’s YJIT is Production-Ready - Shopify

1/17/2023

This post details the advancements in Shopify's YJIT compiler for Ruby, specifically highlighting its production readiness in Ruby 3.2. Key contributions include porting YJIT from C99 to Rust for improved safety and maintainability, significant reductions in memory usage (to approximately one-third of 3.1's overhead), and performance improvements leading to a 38% speedup on railsbench over the Ruby 3.2 interpreter. The post also announces the addition of ARM64 support, enabling native execution on platforms like Apple M1/M2 and Raspberry Pis. Further enhancements include faster instance variable access via object shapes, improved YJIT exit tracing, better support for various Ruby method calls, finer-grained constant cache invalidation, and the allocation of variable-sized objects for improved memory usage and performance. Shopify has successfully deployed YJIT globally on its SFR infrastructure, achieving 5-10% speedups.

2022

Year in Review 2022: Tenderlove's Ruby and Rails Reflections and Predictions - Shopify

12/22/2022

This post details the YJIT team's experience porting the YJIT Ruby compiler to Rust, resulting in a more maintainable codebase and improved machine code generation. It also highlights YJIT's new capability to generate machine code for ARM platforms, enabling its use on ARM servers and Apple Silicon development machines.

When is JIT Faster Than A Compiler? - Shopify

8/11/2022

This post introduces YJIT, a Just-In-Time compiler for Ruby, and explains the fundamental differences between interpreters, ahead-of-time compilers, and JIT compilers. It details how JIT compilers can achieve performance gains by making runtime assumptions about code behavior (e.g., method definitions) and de-optimizing when these assumptions are violated, which is particularly beneficial for dynamic languages like Ruby where code can be redefined at runtime. The post highlights the challenges of compiling Ruby due to its dynamic nature and explains how YJIT addresses these by selectively compiling code and invalidating it when necessary. It also provides information on how to try out YJIT and its current development status.

Shopify and Open Source: A Mutually Beneficial Relationship - Shopify

7/6/2022

This post details Shopify's ongoing investment in Ruby and Rails, highlighting contributions to performance improvements such as YJIT, variable-width allocation, and object shapes. It also mentions collaboration with Oracle on TruffleRuby and funding for Ruby performance research.

Shopify Invests in Research for Ruby at Scale - Shopify

5/16/2022

This post details Shopify's investment in academic research to advance Ruby performance. It highlights funding for Professor Laurence Tratt's work on automatically generating JIT compilers, Professor Steve Blackburn's group on the MMTk garbage collection toolkit for Ruby, and Dr. Stefan Marr's research on improving interpreter startup and warmup times. The goal is to align academic research with the practical needs of large-scale Ruby applications, such as frequent redeployments, and to foster long-term community growth.

Our Experience Porting the YJIT Ruby Compiler to Rust - Shopify

5/11/2022

This post details the experience of porting the YJIT compiler from C99 to Rust. It highlights the challenges of managing complexity in C, the benefits of Rust's type safety, pattern matching, and macro system for compiler development, and the integration process with the CRuby codebase. It also discusses challenges encountered with Cargo's offline build capabilities and bindgen's limitations when interfacing with large C codebases.

Code Ranges: A Deeper Look at Ruby Strings - Shopify

4/14/2022

This post delves into the internal implementation of 'code ranges' within Ruby's string handling, explaining how this mechanism caches information about character encoding validity and byte ranges to optimize string operations. It details the four code range values (UNKNOWN, 7BIT, VALID, BROKEN), their mapping to Ruby methods, and their representation in different Ruby implementations (MRI, JRuby, TruffleRuby). The post also discusses how code ranges are calculated lazily or eagerly and how operations on strings with different code ranges affect performance and potential optimizations, particularly in MRI's approach to avoiding unnecessary scans.

After the Refactor: A Path to Faster Rendering with Liquid-C - Shopify

3/10/2022

This post details a refactor of the Liquid-C Virtual Machine to remove the constant pointer from its bytecode instructions. This change allows for more flexible instruction pointer movement, which is a prerequisite for implementing control flow tags like 'If' and 'For'. The refactor involves storing the index of constants in a hash table within the constants array, reducing duplicate elements and decreasing the size of the constants array. While this refactor slightly increased memory usage and parsing time, it is presented as a stepping stone for future performance improvements by enabling the implementation of control flow tags.

RubyConf 2021: The Talks You Might Have Missed - Shopify

3/2/2022

This post details the RubyConf 2021 talks relevant to Shopify's engineering efforts. Specifically, it highlights Maxime Chevalier Boisvert's talk on 'Building a New JIT Compiler Inside CRuby' which discusses YJIT, an open-source project to incrementally build a new JIT compiler inside CRuby. The talk covers YJIT's key advantages, implementation approach, and early performance results, emphasizing its objective to produce speedups on real-world software like Ruby on Rails while maintaining high compatibility and supporting the latest Ruby features. The inclusion of YJIT in Ruby 3.1 is also noted.

2021

Nerd Out on 10 of Our Favorite Posts From 2021 - Shopify

12/29/2021

This post announces the merging of YJIT into CRuby and highlights its importance to Shopify and the broader Ruby community. It also references additional posts for further reading on YJIT.

GitHub Does My Operations Homework: A Ruby Speed Story - Shopify

10/19/2021

This post details the setup of a continuous integration pipeline using GitHub Actions and Jekyll to automate the benchmarking and public reporting of YJIT performance. It covers the technical challenges and solutions for setting up Jekyll for GitHub Pages, integrating with AWS for benchmark execution, and generating various types of graphs (D3.js, SVG, matplotlib) from benchmark results. The post also highlights the importance of performance monitoring and the automation of operational tasks.

Try Out YJIT for Faster Rubying - Shopify

10/15/2021

This post details how to build and enable YJIT from source, including necessary prerequisites and version manager integration. It provides methods to verify YJIT installation and usage, suggests benchmarking approaches, and discusses the current state of YJIT's production readiness based on early testing. It also explains how to enable detailed runtime statistics for debugging and performance analysis.

YJIT: Building a New JIT Compiler for CRuby - Shopify

10/14/2021

This post details the development of YJIT, a new Just-in-Time (JIT) compiler for CRuby, built using the Basic Block Versioning (BBV) architecture. It highlights the motivation for YJIT due to the limitations of MJIT in real-world applications like Rails. The post presents current benchmark results showing significant speedups over the CRuby interpreter and discusses the advantages of BBV for dynamically-typed code, including specialization based on type information and runtime patching. It also outlines next steps, including merging YJIT into Ruby 3.1 and potential future improvements to CRuby's architecture to further enhance JIT performance.

Diggin’ and Fetchin’ with TruffleRuby - Shopify

8/20/2021

This post details an investigation into a performance issue found in TruffleRuby, an alternative Ruby implementation. The investigation began with refactoring a clever but complex Ruby hash access pattern, leading to the exploration of `Hash#fetch` and `Hash#dig`. The author then prototyped a new method, `dig_fetch`, combining the flexibility of `fetch` with the traversal of `dig`, first by monkey-patching and then by contributing to TruffleRuby itself. Benchmarking revealed a significant performance degradation in `dig` and the prototype `dig_fetch` at deeper nesting levels, suggesting recursion might be a bottleneck and prompting further investigation into iterative solutions and compiler optimizations within TruffleRuby.

2020

Understanding Programs Using Graphs - Shopify

6/2/2020

This post introduces the concept of sea-of-nodes graphs as an intermediate representation used by the TruffleRuby JIT compiler. It explains the structure of these graphs, differentiating between control flow (red arrows) and data flow (green arrows), and between imperative (red boxes) and pure (green boxes) operations. It demonstrates how these graphs enable optimizations like de-duplication of common expressions (global value numbering) and floating expressions, and how they represent loops. The post also discusses the challenges of programming directly with graphs and highlights Shopify's efforts to build tools for visualizing and decompiling these graphs back to Ruby code to aid developer understanding of compiler optimizations.

Optimizing Ruby Lazy Initialization in TruffleRuby with Deoptimization - Shopify

3/31/2020

Introduced an optimization for Ruby's lazy initialization (`||=`) operator in TruffleRuby. This optimization leverages deoptimization by replacing the standard `OrNode` with an `OrLazyValueDefinedNode`. The new node counts the executions of the right-hand side of the `||=` operator and triggers deoptimization if it's executed less than twice. This reduces the amount of machine code generated and speeds up compilation for this common idiom, improving performance and memory usage.

2015

Tuning Ruby's Global Method Cache - Shopify

1/21/2015

This post details the identification and resolution of a performance bottleneck in Ruby's MRI implementation related to the global method cache. The author used `perf` and `ftrace` user probes to analyze the `st_lookup` function and discovered that the default 2048-entry global method cache was insufficient for Shopify's large application. A new environment variable, `RUBY_GLOBAL_METHOD_CACHE_SIZE`, was introduced upstream to allow dynamic configuration of the cache size, and testing showed that increasing the cache size to around 64K-128K resulted in a ~3% cycle savings.