From: "headius (Charles Nutter)" Date: 2013-04-03T07:20:27+09:00 Subject: [ruby-core:53907] [ruby-trunk - Feature #6154] Eliminate extending WaitReadable/Writable at runtime Issue #6154 has been updated by headius (Charles Nutter). File eagain_readwrite.diff added Ok, here's the updated patch. Here's a list of changes from the last time I updated: * Names have been changed to EAGAINWaitReadable, etc, from EAGAINReadable. It seemed cleared to indicate the full name of the module, especially in the SSL case where it would have been SSLErrorReadable. Weird. * OpenSSL has been patched to have SSLErrorWaitReadable and SSLErrorWaitWritable. Note that I am not married to any of these names, if there's better suggestions. * Socket has been patched to use rb_readwrite_sys_fail for EINPROGRESS + WaitWritable. I added EINPROGRESSWaitWritable and EINPROGRESSWaitReadable because it uses rb_readwrite_sys_fail and I didn't think that method should only handle the writable version * rb_readwrite_sys_fail has been expanded to include EINPROGRESS versions of WaitReadable and WaitWritable. Note this is added as a public API, to give users a path away from rb_mod_sys_fail*. * Logic that switches based on ERRNO modified to use switch/case * Tests that checked for exact error types of EAGAIN have been modified to check for the WaitReadable/Writable subclasses. make test-all passes. Please review and comment. Note that I'd also like to lobby for this change to be applied to 1.9.3, perhaps without the public API. Lots of people are going to be on 1.9.3 for a long time before moving to 2.0.x, and they'd like to have this improvement. ---------------------------------------- Feature #6154: Eliminate extending WaitReadable/Writable at runtime https://bugs.ruby-lang.org/issues/6154#change-38121 Author: headius (Charles Nutter) Status: Assigned Priority: Normal Assignee: headius (Charles Nutter) Category: core Target version: next minor 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/