Issue #6154 has been reported by Charles Nutter.

----------------------------------------
Bug #6154: Eliminate extending WaitReadable/Writable at runtime
https://bugs.ruby-lang.org/issues/6154

Author: Charles Nutter
Status: Open
Priority: Normal
Assignee: 
Category: 
Target version: 
ruby -v: 2.0.0dev


The nonblocking IO operations started extending WaitReadable or WaitWritable into the Errno::EAGAIN instance some time during the 1.9 series. This has a rather high cost, since a singleton class must be created and the global method cache must be flushed.

The attached patch instead creates two new classes of the following form, and raises them rather than raising a singleton EAGAIN:

class IO::EAGAINReadable < Errno::EAGAIN
  include WaitReadable
end

class IO::EAGAINWritable < Errno::EAGAIN
  include WaitWritable
end

The performance of repeatedly doing unsuccessful nonblocking reads improves by about 20%:

BEFORE:

system ~/projects/ruby $ ./ruby2.0.0 -rbenchmark -rsocket -e "sock = TCPSocket.new('localhost', 22); 10.times { puts Benchmark.measure { 100_000.times { begin; sock.read_nonblock(10); rescue IO::WaitReadable; end } } }"
  1.210000   0.110000   1.320000 (  1.328921)
  1.220000   0.120000   1.340000 (  1.326136)
  1.220000   0.110000   1.330000 (  1.334026)
  1.230000   0.110000   1.340000 (  1.349927)
  1.310000   0.130000   1.440000 (  1.426608)
  1.210000   0.110000   1.320000 (  1.333530)
  1.220000   0.120000   1.340000 (  1.330352)
  1.230000   0.110000   1.340000 (  1.350455)
  1.220000   0.120000   1.340000 (  1.327550)
  1.220000   0.110000   1.330000 (  1.337785)

AFTER:

system ~/projects/ruby $ ./ruby2.0.0 -rbenchmark -rsocket -e "sock = TCPSocket.new('localhost', 22); 10.times { puts Benchmark.measure { 100_000.times { begin; sock.read_nonblock(10); rescue IO::WaitReadable; end } } }"
  0.980000   0.110000   1.090000 (  1.092166)
  1.010000   0.120000   1.130000 (  1.129877)
  1.090000   0.120000   1.210000 (  1.202066)
  0.960000   0.110000   1.070000 (  1.076274)
  0.970000   0.100000   1.070000 (  1.078000)
  0.970000   0.110000   1.080000 (  1.078156)
  0.970000   0.110000   1.080000 (  1.078005)
  0.970000   0.110000   1.080000 (  1.078266)
  0.980000   0.110000   1.090000 (  1.093039)
  1.000000   0.110000   1.110000 (  1.112519)

This benchmark does not show the hidden cost of constantly invalidating the global method cache.

I also modified a similar case in OpenSSL, where it previously created an SSLError and extended WaitReadable into it.


-- 
http://bugs.ruby-lang.org/