通过模板启用 YJIT 无效

你好,

我运行的是自托管的 Discourse 安装版(2026.5.0-latest)。今天我尝试启用 YJIT。我在 containers/app.yml 中添加了 "templates/enable-ruby-yjit.yml" 并重新构建了应用。

重新构建完成后,发生了一些有趣的事情。在 Docker 容器内,我运行了 env | grep RUBY_YJIT_ENABLE,得到了 RUBY_YJIT_ENABLE=1。到目前为止一切正常。但随后我运行了 sudo -u discourse RAILS_ENV=production bundle exec rails runner 'puts "YJIT enabled: #{RubyVM::YJIT.enabled?}"; puts RUBY_DESCRIPTION',结果却是:

YJIT enabled: false

ruby 3.4.7 (2025-10-08 revision 7a5688e2a2) +PRISM [x86_64-linux] 

尽管添加了 enable-ruby-yjit.yml 模板,YJIT 仍未启用。接着,当我运行 sudo -u discourse RAILS_ENV=production bundle exec rails runner 'puts "GlobalSetting.yjit_enabled=#{GlobalSetting.yjit_enabled}"' 时,得到的是 GlobalSetting.yjit_enabled= —— 一个空值!

总之,在进一步折腾后,我最终通过在 containers/app.yml 中添加以下内容成功启用了 YJIT:

env:
  DISCOURSE_YJIT_ENABLED: true

我确信某处存在一个 bug(GlobalSetting.yjit_enabled 绝不应该返回 nil),但设置环境变量确实生效了。希望有人通过 Google 搜索到这个问题时能发现这个主题。

这难道不是误诊吗?你检查的是一个新启动的 Ruby 进程的 ENV,而不是实际运行 Web 服务器的那个进程。

如果你检查 Pitchfork 主进程的 /proc/<pid>/environ,你会在那里看到 YJIT 环境变量。

root@raspberrypi5:/var/discourse# cat /proc/3331660/environ | tr '\0' '\n' | grep -i yjit
RUBY_YJIT_ENABLE=1

查看环境变量是否存在并不能说明 Ruby 当前是否启用了 YJIT。在本例中,虽然该环境变量已设置,但另有其他因素覆盖了 Rails 用于在启动时启用(或禁用)YJIT 的变量。除此之外,没有其他理由能解释为何 GlobalSetting.yjit_enabled= 即使在新的 Rails 实例中也返回 nil。同样,也没有理由认为 YJIT 在新启动的 Rails 实例中会被禁用。

在将 DISCOURSE_YJIT_ENABLED 环境变量添加到我的 containers/app.yml 后:

  1. 执行 sudo -u discourse RAILS_ENV=production bundle exec rails runner 'puts "YJIT enabled: #{RubyVM::YJIT.enabled?}"; puts RUBY_DESCRIPTION' 返回 YJIT enabled: true
  2. 服务器的内存使用量终于略有上升。
  3. 我的论坛速度明显提升。

复现我的发现应该很容易。只需添加模板并查看即可。

这是错误的。在 Ruby 层面,该环境变量是官方切换开关之一,详见 Ruby 官方文档:

这也是错误的。DISCOURSE_YJIT_ENABLED 仅用于设置 GlobalSetting.yjit_enabledconfig/application.rb 中的 config.yjit。Rails 会据此启用 YJIT,但前提是 YJIT 尚未启用。它不会禁用已经启用的 YJIT。因此,当环境变量已设置时,DISCOURSE_YJIT_ENABLED 实际上不起任何作用。

为证明我的观点,我编写了一个插件,用于返回 Web 进程中 YJIT 是否已启用:

https://discourse-on-a-pi5.falco.dev/ruby-info

你混淆了 Rails 层面的切换开关,这其实没有必要,因为应直接使用 Ruby 层面的切换开关。

嗯!所以你的意思是,我们可以可靠地通过环境变量来检测 YJIT 是否已启用!我之前并不知道这一点,非常感谢你提供的 Ruby 文档链接。

不过我有一点困惑。为什么 env | grep RUBY_YJIT_ENABLE 会返回 RUBY_YJIT_ENABLE=1,但在同一容器中启动一个新的 Rails 实例并执行 #{RubyVM::YJIT.enabled?} 时却输出 false?按理说应该输出 true 才对,毕竟正如你提到的,这个环境变量是在 Ruby 层面起作用的?

你可能非常忙,这个问题其实也不重要,所以不用特意回复。我应该能在自己的服务器上找到答案。顺便问一下,方便分享你的插件吗?我想确保我的 Discourse 服务器确实运行在启用了 YJIT 的 Ruby 环境中。

等我复现成功后,我会将你的回复标记为解决方案。

提前感谢!

很抱歉,我已经解决了问题。

sudo -u discourse 会清除环境变量。使用 -E 标志可以保留正常的环境变量,在这种情况下,运行 sudo -E -u discourse RAILS_ENV=production bundle exec rails runner 'puts "YJIT enabled: #{RubyVM::YJIT.enabled?}"; puts RUBY_DESCRIPTION' 会返回:

YJIT enabled: true
ruby 3.4.7 (2025-10-08 revision 7a5688e2a2) +YJIT +PRISM [x86_64-linux]

只需添加模板即可。

我为浪费了您的时间而道歉,并已将您的回复标记为解决方案。对此深感抱歉。感谢您的调查以及抽出时间回复。