
Issue #19890 has been updated by Dan0042 (Daniel DeLorme). tenderlovemaking (Aaron Patterson) wrote in #note-1:
I think it would be cool if we could push a special frame for methods like `IO#readline` so that we know where user code is, but I feel a solution like that is beyond the scope of this ticket.
Something like that would be great. It's not a critical problem but it pops up frequently enough to be annoying. The same issue happens with regexp pseudo-globals like `$1`, and also with `eval`. It's impossible to instrument `#eval` and `Regexp#match` with pass-thru layers (let's say for performance logging) because it then changes the behavior of the method. ex: ```ruby module LogRegexpPerformance def match(...) = log{ super } def match?(...) = log{ super } def =~(...) = log{ super } prepend_features Regexp end /(a)/ =~ "a" #=> 0 $1 #=> nil ... ooops! ``` I think each stack frame should have a "forward_frame" flag which is set when using `super`. So we can walk up the call stack until we find a frame with forward_frame==0, and set `$_` or `$1` in that stack frame (and possibly in all frames in between?) I imagine calling a method with the `foo(...)` forward-everything syntax could also set the "forward_frame" flag. Then we could have something like: ```ruby class Proxy < BasicObject def initialize(obj) @proxied = obj end def method_missing(...) @proxied.send(...) end end proxy = Proxy.new(io) proxy.gets $_ #=> correct value! ``` ---------------------------------------- Bug #19890: File#realine(chomp: true) slower/more allocations than readline.chomp! https://bugs.ruby-lang.org/issues/19890#change-104666 * Author: segiddins (Samuel Giddins) * Status: Open * Priority: Normal * ruby -v: 3.2.2 * Backport: 3.0: UNKNOWN, 3.1: UNKNOWN, 3.2: UNKNOWN ---------------------------------------- On ruby 3.2.2 running the following script: ``` ruby #!/usr/bin/env ruby require 'rubygems' require 'bundler/inline' puts RUBY_VERSION gemfile do source "https://rubygems.org" gem "benchmark-ipsa" end Benchmark.ipsa do |x| x.report("f.readline(chomp: true)") do File.open("/usr/share/dict/words") do |f| f.readline(chomp: true) until f.eof? end end x.report("f.readline.chomp!") do File.open("/usr/share/dict/words") do |f| until f.eof? s = f.readline s.chomp! s end end end x.report("f.readline.chomp") do File.open("/usr/share/dict/words") do |f| until f.eof? f.readline.chomp end end end x.compare! end ``` I get the following (surprising) result: ``` 3.2.2 Allocations ------------------------------------- f.readline(chomp: true) 707931/1 alloc/ret 50/1 strings/ret f.readline.chomp! 235979/1 alloc/ret 50/1 strings/ret f.readline.chomp 471955/1 alloc/ret 50/1 strings/ret Warming up -------------------------------------- f.readline(chomp: true) 1.000 i/100ms f.readline.chomp! 2.000 i/100ms f.readline.chomp 2.000 i/100ms Calculating ------------------------------------- f.readline(chomp: true) 16.165 (± 6.2%) i/s - 81.000 f.readline.chomp! 25.246 (± 7.9%) i/s - 126.000 f.readline.chomp 20.997 (± 9.5%) i/s - 106.000 Comparison: f.readline.chomp!: 25.2 i/s f.readline.chomp: 21.0 i/s - 1.20x slower f.readline(chomp: true): 16.2 i/s - 1.56x slower ``` I would expect `File#readline(chomp: true)` to be comparable to `s = f.readline; s.chomp!; s` at a bare minimum, but it is slower and has more allocations even than `readline.chomp` -- https://bugs.ruby-lang.org/