irb: pasting in multiline irb is slow

Tried to paste a 31-lines snippet to compare a specific benchmark (anon module’s #inspect was incredibly slow in < 2.7.0).

What I found is that 2.7.0’s irb is unbearably slow to paste even these small snippet.

This is a comparison between Ruby 2.4.3 with pry 0.11.0 and Ruby 2.7.0-preview3 with irb 1.1.0:

https://youtu.be/c9ENYX8VVHA

I did some other tests:

  1. ruby-2.4.3 with irb-0.9.6 and ruby-2.7.0-preview3 with irb-1.1.0 --legacy: absolutely instantaneous;
  2. ruby-2.4.3 with pry-0.11.0: almost instantaneous (I assume parsing for syntax highlighting takes some time); watch video above;
  3. ruby-2.7.0 with irb-1.1.0 and irb-1.2.0 (multiline mode): unbearably slow;

About this issue

  • Original URL
  • State: closed
  • Created 5 years ago
  • Reactions: 122
  • Comments: 29 (3 by maintainers)

Commits related to this issue

Most upvoted comments

I have the same issue with multiline. An additional observation is that the “paste speed” slows even more as the whole payload is gradually pasted.

=> irb --version
irb 1.2.1 (2019-12-24)
=> ruby --version
ruby 2.7.0p0 (2019-12-25 revision 647ee6f091) [x86_64-darwin18]

I’m using;

  • Mac OS Mojave 10.14.6 (18G87) on MacBook Pro (13-inch, 2018)
  • the default Terminal app
  • zsh as my shell
  • ruby managed via rbenv

Below is code that I see appear progressively slower on the screen with each line, when pasted in a single paste action. It takes ~12 seconds to completely paste this into the console.

my_hash = {
  'id1' => { a: 'foo', b: 'bar' },
  'id2' => { a: 'foo', b: 'bar' },
  'id3' => { a: 'foo', b: 'bar' },
  'id4' => { a: 'foo', b: 'bar' },
  'id5' => { a: 'foo', b: 'bar' },
  'id6' => { a: 'foo', b: 'bar' },
  'id7' => { a: 'foo', b: 'bar' },
  'id8' => { a: 'foo', b: 'bar' },
  'id10' => { a: 'foo', b: 'bar' },
  'id11' => { a: 'foo', b: 'bar' },
  'id12' => { a: 'foo', b: 'bar' },
  'id13' => { a: 'foo', b: 'bar' },
  'id14' => { a: 'foo', b: 'bar' },
  'id15' => { a: 'foo', b: 'bar' },
  'id16' => { a: 'foo', b: 'bar' },
  'id17' => { a: 'foo', b: 'bar' },
  'id18' => { a: 'foo', b: 'bar' },
  'id19' => { a: 'foo', b: 'bar' },
  'id20' => { a: 'foo', b: 'bar' }
}

The same full paste operation is instantaneous when using pre-multiline versions of irb (such as 1.0.0, or when running irb --nomultiline.

Workaround

For me, passing --nomultiline wasn’t an option as I was running IRB via the Rails console. The configuration can be configured via an .irbrc file instead;

# Disable 'mutliline' support due to poor pasting performance
# see https://github.com/ruby/irb/issues/43
IRB.conf[:USE_MULTILINE] = false

passing --nomultiline wasn’t an option as I was running IRB via the Rails console

FYI, recent Rails can pass this option like so:

rails console -- --nomultiline

See https://github.com/rails/rails/issues/39909#issuecomment-666412792

I really am rather bemused by the approach here. Yes, I can completely disable all the new stuff in irb because it’s cripplingly slow and it’s helpful to know how. But in that case, why does the feature even exist?

Why is nobody talking about ways to fix the feature’s performance? We’ve had formatting and colouring engines for literally decades that ran many orders of magnitude faster; but it’s 2020, computers are almost unimaginably powerful and we don’t even need something to be coded that well, it just has to be coded to a decent standard.

This is not in any way an unreasonable expectation, surely? That a 2019 MBP 16" with the fastest CPU option should be slowed down to the point where you can watch it paint individual lines in a text only interface is surely acceptable performance for, well, nobody?

2.6.6 vs 2.7.1 irb just on localhost is dreadfully slow.

https://www.youtube.com/watch?v=gFHGwKzHY-4

Even with single line things like puts "1", if I paste those in blocks with a keyboard shortcut and hold it down for autorepeat (as at the start of the video above, but with the multiline string excluded) then IRB under Ruby 2.7 is visibly much slower and, after I let go of the keyboard shortcut, keeps pasting for ages as the terminal is miles behind the keyboard buffer.

It’s 2020 with multi-GHz CPUs and we can’t input characters into a terminal near-instantaneously? Something ain’t right ;-)

@olliebennett workaround solved it for me. by default rails c does not load any other .irbrc but from the current user’s home (~).

so there are 2 ways

  1. create a file in current user’s home (~)

    cd ~ to go to current user’s home (~) run vim .irbrc to create/open file add this line IRB.conf[:USE_MULTILINE] = false press ESC to enter command mode enter command :wq! to save and exit

  2. tell application.rb file to load .irbrc from projects root folder create a .irbrc file in projects root with text IRB.conf[:USE_MULTILINE] = false tell application.rb to use this file instead using

load .irbrc file

def load_console(app=self)
  super
  if File.exists?(project_specific_irbrc = File.join(Rails.root, ".irbrc"))
    puts "Loading project specific .irbrc ..."
    load(project_specific_irbrc)
  end
end

I have the same problem… ruby 2.6 is almost instantaneous, 2.7 very slow, seems like each char is inputted individually.

I’m using Linux + Bash + rvm.

We can’t update to Ruby 3 quite yet but just adding gem "reline", "0.2.2" to our Gemfile helped tremendously.

Thanks for the efforts with this!

Experiencing similar issues, sometimes strings don’t get pasted completely, even crashing irb with usually something like /usr/share/rvm/rubies/ruby-2.7.0/lib/ruby/2.7.0/reline/ansi.rb:76:in 'block in cursor_pos': undefined method 'pre_match' for nil:NilClass (NoMethodError)

Workaround from @olliebennett seems to help.

According to https://www.ruby-lang.org/en/news/2020/12/08/ruby-3-0-0-preview2-released/

Pasting long code to IRB is 53 times faster than bundled with Ruby 2.7.0. For example, the time required to paste this sample code goes from 11.7 seconds to 0.22 seconds.

This slow pasting issue is fixed in Ruby versions >= 3.0

I’ve released the new version of reline gem including ruby/reline#184 and ruby/reline#186 for speeding up. Please install with gem install irb reline and check it.

The IRB bundled with Ruby 3.0 had many bugs, so I fixed them and released irb gem 1.3.1 and reline gem 0.2.1. Please try them by gem install irb reline.

looks like this is fixed, closing it; thanks @aycabta and everyone who pitched in

I believe the issue is known upstream.

Even pasting a single line array with 10 hashes is visibly slow.

require 'time'
start_time = Time.now;nil

[{ id: 1, some_string: 'some_value' },{ id: 2, some_string: 'some_value' },{ id: 3, some_string: 'some_value' },{ id: 4, some_string: 'some_value' },{ id: 5, some_string: 'some_value' },{ id: 6, some_string: 'some_value' },{ id: 7, some_string: 'some_value' },{ id: 8, some_string: 'some_value' },{ id: 9, some_string: 'some_value' },{ id: 10, some_string: 'some_value'}]

end_time = Time.now;nil
puts "Duration: #{end_time - start_time} seconds"

EDIT: improved benchmarking

ruby 2.7.2 with reline 0.1.5 takes around 2.9s ruby 2.7.2 with reline 0.1.8 takes around 0.7s ruby 2.7.2 with --nomultiline takes around 0.01s

So it improved a lot, but still feels not instant for this case.

And I can confirm what @jordan-brough wrote, pasting big hashes like from a JSON response is still really slow or locks up irb. In a really bad case with 200KB of JSON, irb --nomultiline needs 20s to paste it, while irb with reline 0.1.8 crashes after 2min with

Traceback (most recent call last):
	30: from /.rvm/gems/ruby-2.7.2/bin/ruby_executable_hooks:15:in `<main>'
	29: from /.rvm/gems/ruby-2.7.2/bin/ruby_executable_hooks:15:in `eval'
	28: from /.rvm/gems/ruby-2.7.2/bin/irb:23:in `<main>'
	27: from /.rvm/gems/ruby-2.7.2/bin/irb:23:in `load'
	26: from /.rvm/gems/ruby-2.7.2/gems/irb-1.2.7/exe/irb:11:in `<top (required)>'
	25: from /.rvm/gems/ruby-2.7.2/gems/irb-1.2.7/lib/irb.rb:400:in `start'
	24: from /.rvm/gems/ruby-2.7.2/gems/irb-1.2.7/lib/irb.rb:471:in `run'
	23: from /.rvm/gems/ruby-2.7.2/gems/irb-1.2.7/lib/irb.rb:471:in `catch'
	22: from /.rvm/gems/ruby-2.7.2/gems/irb-1.2.7/lib/irb.rb:472:in `block in run'
	21: from /.rvm/gems/ruby-2.7.2/gems/irb-1.2.7/lib/irb.rb:537:in `eval_input'
	20: from /.rvm/gems/ruby-2.7.2/gems/irb-1.2.7/lib/irb/ruby-lex.rb:150:in `each_top_level_statement'
	19: from /.rvm/gems/ruby-2.7.2/gems/irb-1.2.7/lib/irb/ruby-lex.rb:150:in `catch'
	18: from /.rvm/gems/ruby-2.7.2/gems/irb-1.2.7/lib/irb/ruby-lex.rb:151:in `block in each_top_level_statement'
	17: from /.rvm/gems/ruby-2.7.2/gems/irb-1.2.7/lib/irb/ruby-lex.rb:151:in `loop'
	16: from /.rvm/gems/ruby-2.7.2/gems/irb-1.2.7/lib/irb/ruby-lex.rb:154:in `block (2 levels) in each_top_level_statement'
	15: from /.rvm/gems/ruby-2.7.2/gems/irb-1.2.7/lib/irb/ruby-lex.rb:182:in `lex'
	14: from /.rvm/gems/ruby-2.7.2/gems/irb-1.2.7/lib/irb.rb:518:in `block in eval_input'
	13: from /.rvm/gems/ruby-2.7.2/gems/irb-1.2.7/lib/irb.rb:704:in `signal_status'
	12: from /.rvm/gems/ruby-2.7.2/gems/irb-1.2.7/lib/irb.rb:519:in `block (2 levels) in eval_input'
	11: from /.rvm/gems/ruby-2.7.2/gems/irb-1.2.7/lib/irb/input-method.rb:294:in `gets'
	10: from /.rvm/rubies/ruby-2.7.2/lib/ruby/2.7.0/forwardable.rb:235:in `readmultiline'
	 9: from /.rvm/rubies/ruby-2.7.2/lib/ruby/2.7.0/forwardable.rb:235:in `readmultiline'
	 8: from /.rvm/gems/ruby-2.7.2/gems/reline-0.1.8/lib/reline.rb:175:in `readmultiline'
	 7: from /.rvm/gems/ruby-2.7.2/gems/reline-0.1.8/lib/reline.rb:209:in `inner_readline'
	 6: from /.rvm/gems/ruby-2.7.2/gems/reline-0.1.8/lib/reline/line_editor.rb:115:in `reset'
	 5: from /.rvm/gems/ruby-2.7.2/gems/reline-0.1.8/lib/reline/ansi.rb:136:in `cursor_pos'
	 4: from /.rvm/gems/ruby-2.7.2/gems/reline-0.1.8/lib/reline/ansi.rb:136:in `raw'
	 3: from /.rvm/gems/ruby-2.7.2/gems/reline-0.1.8/lib/reline/ansi.rb:146:in `block in cursor_pos'
	 2: from /.rvm/gems/ruby-2.7.2/gems/reline-0.1.8/lib/reline/ansi.rb:146:in `reverse_each'
	 1: from /.rvm/gems/ruby-2.7.2/gems/reline-0.1.8/lib/reline/ansi.rb:147:in `block (2 levels) in cursor_pos'
/.rvm/gems/ruby-2.7.2/gems/reline-0.1.8/lib/reline/ansi.rb:147:in `ungetc': ungetbyte failed (IOError)

I have this issue too, like everyone, but even when it’s a single line — removing the newlines from the json block.

For whatever reason, the suggestions above didn’t work for me. So I wanted to offer another [roundabout] solution:

I simply created a json file and posted it to a cloud storage app (I used S3). Opened the file with json = URI.parse('<url-to-the-file>').open { |f| f.read }. And parsed: json = MultiJson.load(json) (use whatever json tool you’re used to).

It’s slower, but much faster than pasting 😱 Until this gets figured out, this will be my solution.

I’ve managed to reproduce this as well (iTerm2 on 10.14), except for me it alternates between pasting very slowly one character at a time (this bug) or crashing IRB entirely (#46) (and trying to run the paste commands in bash, which is super dangerous). This currently makes Ruby 2.7 completely useless for me, as I can’t easily change the IRB version back to 0.9.6 and can’t use any 2.7 consoles.

I sometimes have to paste the content of a spreadsheet column into IRB (don’t ask why … please, don’t 😖 ). With about 200 rows it takes way to long with syntax highlighting on.

Thanks @Faisal-nfl for the workaround! That helped me out for now.

I think that highlighting and multiline are a great addition to IRB and I hope we can find a way to get it up to speed in the future.

@rubyFeedback Is it logged anywhere?

Looking much better with the latest updates!

Retesting my previous example I get:

  • Ruby 3.0.0 + irb 1.3.1 + reline 0.2.1: 3.00s
  • Ruby 2.7.2 + irb 1.3.1 + reline 0.2.1: 3.90s
  • Ruby 3.0.0 + --nomultiline: 1.48s
  • Ruby 2.6.6: 0.81s

So, still around 3.5x slower than Ruby 2.6.6 for me, but that’s so much better than the previous ~30x slower!

Copy-pasting this example code is still pretty bad for me.

On my computer:

$ gem list irb reline 
*** LOCAL GEMS ***
irb (1.2.7, default: 1.2.3)
*** LOCAL GEMS ***
reline (0.1.6, default: 0.1.3)

40 seconds with multiline:

$ irb
>> # paste example code …
…
Duration: 39.935132 seconds

1 second without multiline:

$ irb --nomultiline
>> # paste example code …
…
Duration: 1.208119 seconds