JRuby, RVM and Vim walk into a (performance) bar

I'm not sure why RVM is still my default Ruby version manager yet the issue at hand should be reproducible on rbenv too.

Let's add a bit of context to this: we have an API written in Ruby and running on JRuby i.e. via JVM which implies slow start-up times compared to CRuby (yes, even with export JRUBY_OPTS='--dev'). I use Vim 8.0.502 which of course comes by default bundled with Tim Pope's vim-ruby .

The problem: since I've switched to JRuby I started to notice a slowdown whilst working in Vim with .rb files. I did not pay too much attention to it at first (read almost for a month) but today it really started to bug me. I had no idea what was causing the slowdown so I tried Vim's trusty command-line option:

vim --startuptime profile-data app/controllers/application_controller.rb

which yielded (edited for brevity):

104.316  002.877  002.877: sourcing /usr/local/share/vim/vim80/syntax/ruby.vim
1228.691  1123.486  1123.486: sourcing /usr/local/share/vim/vim80/ftplugin/ruby.vim
1229.935  000.686  000.686: sourcing /usr/local/share/vim/vim80/indent/ruby.vim
{ edited }
1273.618  043.838: opening buffers
1274.471  000.853: BufEnter autocommands
1274.475  000.004: editing files in windows
1274.924  000.449: VimEnter autocommands
1274.932  000.008: before starting main loop
1299.500  024.568: first screen update
1299.501  000.001: --- VIM STARTED ---

Over one second to open a simple Ruby file? auch!

The relevant line in that file is:

1228.691  1123.486  1123.486: sourcing /usr/local/share/vim/vim80/ftplugin/ruby.vim

Why are we spending one second in ruby.vim ? the answer is in this function which gets executed before opening a .rb file:

function! s:query_path(root) abort
  let code = "print $:.join %q{,}"
  if &shell =~# 'sh' && empty(&shellxquote)
    let prefix = 'env PATH='.shellescape($PATH).' '
  else
    let prefix = ''
  endif
  if &shellxquote == "'"
    let path_check = prefix.'ruby --disable-gems -e "' . code . '"'
  else
    let path_check = prefix."ruby --disable-gems -e '" . code . "'"
  endif

Note: ftplugin stands for filetype and not FTP - so yes, this logic is required.

After all of this I did one more test: using top in another tmux window I could actually see the Java process running after Vim therefore the issue was trivial: vim-ruby was using JRuby.

The logical next step: instruct Vim to use CRuby if RVM with JRuby is detected. After some googling around the solution was an appendix to .vimrc:

if !empty(matchstr($MY_RUBY_HOME, 'jruby'))
  let g:ruby_path = '/usr/bin/ruby'
endif

Basically if RVM has set-up JRuby already instruct vim-ruby via its helpful ruby_path config to use the system's default Ruby (on macOS that being - CRuby version 2.0.0p648)

Results after the fix:

104.643  002.876  002.876: sourcing /usr/local/share/vim/vim80/syntax/ruby.vim
106.938  001.369  001.369: sourcing /usr/local/share/vim/vim80/ftplugin/ruby.vim
108.062  000.622  000.622: sourcing /usr/local/share/vim/vim80/indent/ruby.vim
{ edited }
152.700  044.957: opening buffers
153.389  000.689: BufEnter autocommands
153.394  000.005: editing files in windows
154.086  000.692: VimEnter autocommands
154.092  000.006: before starting main loop
178.172  024.080: first screen update
178.174  000.002: --- VIM STARTED ---

~200ms => much better.

Tagged under: