Table of contents
    blog cover

    How to customize YJIT in the Rails app

    Software Engineer
    Software Engineer
    Ruby on Rails
    Ruby on Rails
    In this post, we'll dive into what YJIT is, how to enable it in your Rails app, and how to monitor and configure it for optimal performance.

    What is YJIT?

    YJIT is a lightweight Just-In-Time (JIT) compiler introduced in Ruby to speed up method execution by compiling Ruby code into machine code on the fly. This approach can significantly boost performance for Ruby applications, including those built with Rails.

    YJIT may not be suitable for certain applications. It currently only supports macOS, Linux and BSD on x86-64 and arm64/aarch64 CPUs. YJIT will use more memory than the Ruby interpreter because the JIT compiler needs to generate machine code in memory and maintain additional state information.
    That's why we have to customize it to take advantage of the speed and avoid memory overflow.

    Enabling YJIT in Your Rails Application

    To leverage YJIT in your Rails app, follow these steps:

    1. Install Ruby 3.3.x: Ensure you're running Ruby 3.3.x or later, as YJIT's performance improvements are most effective than 3.2
    Make sure you install Rust because Ruby requires it to compile YJIT.

    2. Enable YJIT: You can enable YJIT by setting an environment variable or using command-line options:

    // language: bash
    RUBY_YJIT_ENABLE=1

    Or start your Rails server with:
    // language: bash
    RUBYOPT="--yjit" rails s

    Enable YJIT at Runtime: For better boot performance, you can disable YJIT at startup and enable it later in your application:
    // language: ruby
    RubyVM::YJIT.enable
    From Rails 7.2 with Ruby 3.3+, YJIT is enabled by default. This approach prevents YJIT from compiling initialization code, which typically doesn't benefit from JIT compilation.

    Monitoring YJIT's Performance

    To gain insights into YJIT's performance in your Rails app, integrate a custom middleware to log runtime statistics. Here's a sample logger you can use:

    // language: ruby
    class YJITLogger
      def initialize(app)
        @app = app
      end
    
      def call(env)
        # Sample approximately 5% of requests
        if rand < 0.05 && defined?(RubyVM::YJIT) && RubyVM::YJIT.enabled?
          stats = RubyVM::YJIT.runtime_stats
          Rails.logger.info "YJIT - code region size: #{stats[:code_region_size]}"
          Rails.logger.info "YJIT - ratio in YJIT: #{stats[:ratio_in_yjit]}"
        end
        @app.call(env)
      end
    end
    
    Rails.application.config.middleware.use YJITLogger

    This middleware logs key YJIT statistics, such as the size of the code region used by JIT-compiled code and the ratio of instructions executed by YJIT versus the Ruby interpreter.

    Configuring YJIT for Better Performance

    YJIT comes with several configuration options that can affect its performance. Here are some tips to optimize YJIT for your Rails app:

    1. Adjust Executable Memory Size: The default memory size for YJIT is 48 MiB (you can read more in this
    document
    ), but increasing it can improve performance. For instance, setting it to 64 - 96 MiB might benefit applications with larger codebases:
    // language: bash
    RUBYOPT="--yjit-exec-mem-size=64"
    Use the RubyVM::YJIT.runtime_stats[:code_region_size] metric to ensure your code region size stays below this limit.

    2. Monitor Ratio in YJIT: Aim for a high ratio_in_yjit value, ideally above 98%, indicating that most of your code is being executed by YJIT.

    3. Use jemalloc for Memory Management: jemalloc is a memory allocator that can improve memory usage patterns and reduce fragmentation in Ruby applications. It's often used in production environments to enhance performance and stability. By using jemalloc, you can help your application run with higher --yjit-exec-mem-size setting

    Conclusion

    By enabling and configuring YJIT, you can achieve substantial performance improvements in your Rails application. DON'T FORGET to monitor key metrics (response time, memory, ...) and adjust configurations. There is not one fit solution for all projects.


    References:
    Created at 2024-12-04 16:29:15 +0700

    Related blogs