ruby-core@ruby-lang.org archive (unofficial mirror)
 help / color / mirror / Atom feed
* [ruby-core:43325] [ruby-trunk - Bug #6154][Open] Eliminate extending WaitReadable/Writable at runtime
@ 2012-03-16  4:02 Charles Nutter
  2012-03-16  4:05 ` [ruby-core:43326] [ruby-trunk - Bug #6154] " Charles Nutter
                   ` (17 more replies)
  0 siblings, 18 replies; 25+ messages in thread
From: Charles Nutter @ 2012-03-16  4:02 UTC (permalink / raw
  To: ruby-core


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/

^ permalink raw reply	[flat|nested] 25+ messages in thread

* [ruby-core:43326] [ruby-trunk - Bug #6154] Eliminate extending WaitReadable/Writable at runtime
  2012-03-16  4:02 [ruby-core:43325] [ruby-trunk - Bug #6154][Open] Eliminate extending WaitReadable/Writable at runtime Charles Nutter
@ 2012-03-16  4:05 ` Charles Nutter
  2012-03-17  0:24   ` [ruby-core:43361] " Eric Wong
  2012-03-17 15:27 ` [ruby-core:43369] Re: [ruby-trunk - Bug #6154][Open] " Tanaka Akira
                   ` (16 subsequent siblings)
  17 siblings, 1 reply; 25+ messages in thread
From: Charles Nutter @ 2012-03-16  4:05 UTC (permalink / raw
  To: ruby-core


Issue #6154 has been updated by Charles Nutter.


I should have mentioned that JRuby has been running this way for a while (on master) and we have had no reports of incompatibility. The concrete subclass is_a EAGAIN and is_a WaitReadable, so all typical exception-handling patterns work correctly. The only cases that break are cases that check e.class == EAGAIN, which is probably not a good pattern anyway.
----------------------------------------
Bug #6154: Eliminate extending WaitReadable/Writable at runtime
https://bugs.ruby-lang.org/issues/6154#change-24631

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/

^ permalink raw reply	[flat|nested] 25+ messages in thread

* [ruby-core:43361] Re: [ruby-trunk - Bug #6154] Eliminate extending WaitReadable/Writable at runtime
  2012-03-16  4:05 ` [ruby-core:43326] [ruby-trunk - Bug #6154] " Charles Nutter
@ 2012-03-17  0:24   ` Eric Wong
  2012-03-17  8:56     ` [ruby-core:43366] " Charles Oliver Nutter
  0 siblings, 1 reply; 25+ messages in thread
From: Eric Wong @ 2012-03-17  0:24 UTC (permalink / raw
  To: ruby-core

Charles Nutter <headius@headius.com> wrote:
> I should have mentioned that JRuby has been running this way for a
> while (on master) and we have had no reports of incompatibility. The
> concrete subclass is_a EAGAIN and is_a WaitReadable, so all typical
> exception-handling patterns work correctly.

Good to know.  I like this change[1]

> The only cases that break are cases that check e.class == EAGAIN,
> which is probably not a good pattern anyway.

I also noticed some test failures with your patch.  (But I think
the minor incompatibility is acceptable for 2.0.0)

test_read_nonblock(OpenSSL::TestPair) [$top_srcdir/test/openssl/test_pair.rb:145]:
[OpenSSL::SSL::SSLError] exception expected, not
Class: <OpenSSL::SSL::SSLErrorReaable>
Message: <"read would block">
---Backtrace---
$top_srcdir/trunk/.ext/common/openssl/buffering.rb:174:in `sysread_nonblock'
$top_srcdir/trunk/.ext/common/openssl/buffering.rb:174:in `read_nonblock'
$top_srcdir/test/openssl/test_pair.rb:147:in `block (2 levels) in test_read_nonblock'
---------------

test_dgram_pair(TestSocket_UNIXSocket) [$top_srcdir/test/socket/test_unix.rb:348]:
[Errno::EAGAIN] exception expected, not
Class: <IO::EAGAINReadable>
Message: <"Resource temporarily unavailable - recvfrom(2) would block">
---Backtrace---
$top_srcdir/test/socket/test_unix.rb:348:in `recv_nonblock'
$top_srcdir/test/socket/test_unix.rb:348:in `block in test_dgram_pair'
---------------

[1] I'd like an exception-avoiding API more, though :)

^ permalink raw reply	[flat|nested] 25+ messages in thread

* [ruby-core:43366] Re: [ruby-trunk - Bug #6154] Eliminate extending WaitReadable/Writable at runtime
  2012-03-17  0:24   ` [ruby-core:43361] " Eric Wong
@ 2012-03-17  8:56     ` Charles Oliver Nutter
  0 siblings, 0 replies; 25+ messages in thread
From: Charles Oliver Nutter @ 2012-03-17  8:56 UTC (permalink / raw
  To: ruby-core

On Fri, Mar 16, 2012 at 7:24 PM, Eric Wong <normalperson@yhbt.net> wrote:
>> The only cases that break are cases that check e.class == EAGAIN,
>> which is probably not a good pattern anyway.
>
> I also noticed some test failures with your patch.  (But I think
> the minor incompatibility is acceptable for 2.0.0)
>
> test_read_nonblock(OpenSSL::TestPair) [$top_srcdir/test/openssl/test_pair.rb:145]:
> [OpenSSL::SSL::SSLError] exception expected, not
> Class: <OpenSSL::SSL::SSLErrorReaable>
...
> test_dgram_pair(TestSocket_UNIXSocket) [$top_srcdir/test/socket/test_unix.rb:348]:
> [Errno::EAGAIN] exception expected, not
> Class: <IO::EAGAINReadable>

Yes, these are expected failures in my eyes, and not indicative of
typical exception-handling or nonblocking-IO use cases.

And I agree 100% about adding an exception-free nonblocking API. This
plasters over the problems 1.9.x introduced, but doesn't solve the
root cause.

- Charlie

^ permalink raw reply	[flat|nested] 25+ messages in thread

* [ruby-core:43369] Re: [ruby-trunk - Bug #6154][Open] Eliminate extending WaitReadable/Writable at runtime
  2012-03-16  4:02 [ruby-core:43325] [ruby-trunk - Bug #6154][Open] Eliminate extending WaitReadable/Writable at runtime Charles Nutter
  2012-03-16  4:05 ` [ruby-core:43326] [ruby-trunk - Bug #6154] " Charles Nutter
@ 2012-03-17 15:27 ` Tanaka Akira
  2012-03-17 22:33   ` [ruby-core:43377] " Charles Oliver Nutter
  2012-03-22 17:37 ` [ruby-core:43560] [ruby-trunk - Bug #6154] " headius (Charles Nutter)
                   ` (15 subsequent siblings)
  17 siblings, 1 reply; 25+ messages in thread
From: Tanaka Akira @ 2012-03-17 15:27 UTC (permalink / raw
  To: ruby-core

2012/3/16 Charles Nutter <headius@headius.com>:

> 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:

EWOULDBLOCK is a different from EAGAIN on some platforms.
(HP-UX, for example.)

I think your patch breaks applications which rescue only EWOULDBLOCK,
on such platforms.
-- 
Tanaka Akira

^ permalink raw reply	[flat|nested] 25+ messages in thread

* [ruby-core:43377] Re: [ruby-trunk - Bug #6154][Open] Eliminate extending WaitReadable/Writable at runtime
  2012-03-17 15:27 ` [ruby-core:43369] Re: [ruby-trunk - Bug #6154][Open] " Tanaka Akira
@ 2012-03-17 22:33   ` Charles Oliver Nutter
  2012-03-18 15:16     ` [ruby-core:43453] " Hiroshi Nakamura
  0 siblings, 1 reply; 25+ messages in thread
From: Charles Oliver Nutter @ 2012-03-17 22:33 UTC (permalink / raw
  To: ruby-core

[-- Attachment #1: Type: text/plain, Size: 849 bytes --]

I thought about that...it would still be better to add EWOULDBLOCKReadable,
etc, than to pay the .extend toll every time.
On Mar 17, 2012 10:28 AM, "Tanaka Akira" <akr@fsij.org> wrote:

> 2012/3/16 Charles Nutter <headius@headius.com>:
>
> > 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:
>
> EWOULDBLOCK is a different from EAGAIN on some platforms.
> (HP-UX, for example.)
>
> I think your patch breaks applications which rescue only EWOULDBLOCK,
> on such platforms.
> --
> Tanaka Akira
>
>

[-- Attachment #2: Type: text/html, Size: 1172 bytes --]

^ permalink raw reply	[flat|nested] 25+ messages in thread

* [ruby-core:43453] Re: [ruby-trunk - Bug #6154][Open] Eliminate extending WaitReadable/Writable at runtime
  2012-03-17 22:33   ` [ruby-core:43377] " Charles Oliver Nutter
@ 2012-03-18 15:16     ` Hiroshi Nakamura
  0 siblings, 0 replies; 25+ messages in thread
From: Hiroshi Nakamura @ 2012-03-18 15:16 UTC (permalink / raw
  To: ruby-core

Charles, do you create a patch to introduce 4 constants (+2 for
EWOULDBLOCK) ?  We discussed this issue today and we concluded that
that's better than anonymous class caching (to avoid Marshalling
issue.)

I think I can take care of openssl change.

^ permalink raw reply	[flat|nested] 25+ messages in thread

* [ruby-core:43560] [ruby-trunk - Bug #6154] Eliminate extending WaitReadable/Writable at runtime
  2012-03-16  4:02 [ruby-core:43325] [ruby-trunk - Bug #6154][Open] Eliminate extending WaitReadable/Writable at runtime Charles Nutter
  2012-03-16  4:05 ` [ruby-core:43326] [ruby-trunk - Bug #6154] " Charles Nutter
  2012-03-17 15:27 ` [ruby-core:43369] Re: [ruby-trunk - Bug #6154][Open] " Tanaka Akira
@ 2012-03-22 17:37 ` headius (Charles Nutter)
  2012-03-22 18:38 ` [ruby-core:43562] " headius (Charles Nutter)
                   ` (14 subsequent siblings)
  17 siblings, 0 replies; 25+ messages in thread
From: headius (Charles Nutter) @ 2012-03-22 17:37 UTC (permalink / raw
  To: ruby-core


Issue #6154 has been updated by headius (Charles Nutter).


Ok, will do.
----------------------------------------
Bug #6154: Eliminate extending WaitReadable/Writable at runtime
https://bugs.ruby-lang.org/issues/6154#change-25049

Author: headius (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/

^ permalink raw reply	[flat|nested] 25+ messages in thread

* [ruby-core:43562] [ruby-trunk - Bug #6154] Eliminate extending WaitReadable/Writable at runtime
  2012-03-16  4:02 [ruby-core:43325] [ruby-trunk - Bug #6154][Open] Eliminate extending WaitReadable/Writable at runtime Charles Nutter
                   ` (2 preceding siblings ...)
  2012-03-22 17:37 ` [ruby-core:43560] [ruby-trunk - Bug #6154] " headius (Charles Nutter)
@ 2012-03-22 18:38 ` headius (Charles Nutter)
  2012-11-16 16:27 ` [ruby-core:49437] [ruby-trunk - Feature " headius (Charles Nutter)
                   ` (13 subsequent siblings)
  17 siblings, 0 replies; 25+ messages in thread
From: headius (Charles Nutter) @ 2012-03-22 18:38 UTC (permalink / raw
  To: ruby-core


Issue #6154 has been updated by headius (Charles Nutter).

File eagain_readwrite.diff added

Updated patch with the following changes:

* rb_eEWOULDBLOCKReadable and rb_eEWOULDBLOCKWritable added
* EWOULDBLOCK versions are set to EAGAIN versions if EAGAIN == EWOULDBLOCK
* renamed function rb_readwrite_sys_fail to match error.c names better
* reverted OSSL changes so nahi can do them right
* fixed the one test (test/socket/test_unix.rb) that expected an exact exception

Notes:

* assert_raise should probably use === instead of == to better match rescue logic
* more tests for this logic are needed(!!!)
* I have not tested RubySpec yet
* I can't test on HP/UX or other systems where EAGAIN != EWOULDBLOCK
----------------------------------------
Bug #6154: Eliminate extending WaitReadable/Writable at runtime
https://bugs.ruby-lang.org/issues/6154#change-25051

Author: headius (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/

^ permalink raw reply	[flat|nested] 25+ messages in thread

* [ruby-core:49437] [ruby-trunk - Feature #6154] Eliminate extending WaitReadable/Writable at runtime
  2012-03-16  4:02 [ruby-core:43325] [ruby-trunk - Bug #6154][Open] Eliminate extending WaitReadable/Writable at runtime Charles Nutter
                   ` (3 preceding siblings ...)
  2012-03-22 18:38 ` [ruby-core:43562] " headius (Charles Nutter)
@ 2012-11-16 16:27 ` headius (Charles Nutter)
  2012-11-18  4:44 ` [ruby-core:49517] " headius (Charles Nutter)
                   ` (12 subsequent siblings)
  17 siblings, 0 replies; 25+ messages in thread
From: headius (Charles Nutter) @ 2012-11-16 16:27 UTC (permalink / raw
  To: ruby-core


Issue #6154 has been updated by headius (Charles Nutter).


Seven months and no activity. This is not a breaking change and it could improve performance of nonblocking IO operations a lot. Any reason not to incorporate it?
----------------------------------------
Feature #6154: Eliminate extending WaitReadable/Writable at runtime
https://bugs.ruby-lang.org/issues/6154#change-32974

Author: headius (Charles Nutter)
Status: Assigned
Priority: Normal
Assignee: matz (Yukihiro Matsumoto)
Category: 
Target version: 


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/

^ permalink raw reply	[flat|nested] 25+ messages in thread

* [ruby-core:49517] [ruby-trunk - Feature #6154] Eliminate extending WaitReadable/Writable at runtime
  2012-03-16  4:02 [ruby-core:43325] [ruby-trunk - Bug #6154][Open] Eliminate extending WaitReadable/Writable at runtime Charles Nutter
                   ` (4 preceding siblings ...)
  2012-11-16 16:27 ` [ruby-core:49437] [ruby-trunk - Feature " headius (Charles Nutter)
@ 2012-11-18  4:44 ` headius (Charles Nutter)
  2012-11-24  4:51 ` [ruby-core:50011] " mame (Yusuke Endoh)
                   ` (11 subsequent siblings)
  17 siblings, 0 replies; 25+ messages in thread
From: headius (Charles Nutter) @ 2012-11-18  4:44 UTC (permalink / raw
  To: ruby-core


Issue #6154 has been updated by headius (Charles Nutter).


I have made the additional OpenSSL changes in the following JRuby commit: https://github.com/jruby/jruby/commit/8b022c896ea0d76f876e458229f4f150893295b9

The new exceptions are SSLErrorReadable and SSLErrorWritable. I am not married to these names, but unfortunately I do not wish to continue perpetuating the performance impact of extending these exceptions every time.

The current behavior of MRI to extend WaitReadable and WaitWritable every time is a *bug*. It needs to be fixed.
----------------------------------------
Feature #6154: Eliminate extending WaitReadable/Writable at runtime
https://bugs.ruby-lang.org/issues/6154#change-33050

Author: headius (Charles Nutter)
Status: Assigned
Priority: Normal
Assignee: matz (Yukihiro Matsumoto)
Category: 
Target version: 


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/

^ permalink raw reply	[flat|nested] 25+ messages in thread

* [ruby-core:50011] [ruby-trunk - Feature #6154] Eliminate extending WaitReadable/Writable at runtime
  2012-03-16  4:02 [ruby-core:43325] [ruby-trunk - Bug #6154][Open] Eliminate extending WaitReadable/Writable at runtime Charles Nutter
                   ` (5 preceding siblings ...)
  2012-11-18  4:44 ` [ruby-core:49517] " headius (Charles Nutter)
@ 2012-11-24  4:51 ` mame (Yusuke Endoh)
  2012-12-12  0:15   ` [ruby-core:50798] " Eric Wong
  2013-03-15 17:24 ` [ruby-core:53449] " headius (Charles Nutter)
                   ` (10 subsequent siblings)
  17 siblings, 1 reply; 25+ messages in thread
From: mame (Yusuke Endoh) @ 2012-11-24  4:51 UTC (permalink / raw
  To: ruby-core


Issue #6154 has been updated by mame (Yusuke Endoh).

Target version set to next minor

I postpone this ticket to next minor.  Very sorry.
If Eric Wong were a committer, I would leave this to him.

-- 
Yusuke Endoh <mame@tsg.ne.jp>
----------------------------------------
Feature #6154: Eliminate extending WaitReadable/Writable at runtime
https://bugs.ruby-lang.org/issues/6154#change-33785

Author: headius (Charles Nutter)
Status: Assigned
Priority: Normal
Assignee: matz (Yukihiro Matsumoto)
Category: 
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/

^ permalink raw reply	[flat|nested] 25+ messages in thread

* [ruby-core:50798] Re: [ruby-trunk - Feature #6154] Eliminate extending WaitReadable/Writable at runtime
  2012-11-24  4:51 ` [ruby-core:50011] " mame (Yusuke Endoh)
@ 2012-12-12  0:15   ` Eric Wong
  0 siblings, 0 replies; 25+ messages in thread
From: Eric Wong @ 2012-12-12  0:15 UTC (permalink / raw
  To: ruby-core

"mame (Yusuke Endoh)" <mame@tsg.ne.jp> wrote:
> I postpone this ticket to next minor.  Very sorry.
> If Eric Wong were a committer, I would leave this to him.

Looks like Charles Nutter will be a committer soon and can handle this.

^ permalink raw reply	[flat|nested] 25+ messages in thread

* [ruby-core:53449] [ruby-trunk - Feature #6154] Eliminate extending WaitReadable/Writable at runtime
  2012-03-16  4:02 [ruby-core:43325] [ruby-trunk - Bug #6154][Open] Eliminate extending WaitReadable/Writable at runtime Charles Nutter
                   ` (6 preceding siblings ...)
  2012-11-24  4:51 ` [ruby-core:50011] " mame (Yusuke Endoh)
@ 2013-03-15 17:24 ` headius (Charles Nutter)
  2013-03-16  1:05 ` [ruby-core:53474] " drbrain (Eric Hodel)
                   ` (9 subsequent siblings)
  17 siblings, 0 replies; 25+ messages in thread
From: headius (Charles Nutter) @ 2013-03-15 17:24 UTC (permalink / raw
  To: ruby-core


Issue #6154 has been updated by headius (Charles Nutter).


mame or matz: Is this change approved? If so, I can proceed with a patch that includes what I've already posted plus OpenSSL changes.
----------------------------------------
Feature #6154: Eliminate extending WaitReadable/Writable at runtime
https://bugs.ruby-lang.org/issues/6154#change-37633

Author: headius (Charles Nutter)
Status: Assigned
Priority: Normal
Assignee: matz (Yukihiro Matsumoto)
Category: 
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/

^ permalink raw reply	[flat|nested] 25+ messages in thread

* [ruby-core:53474] [ruby-trunk - Feature #6154] Eliminate extending WaitReadable/Writable at runtime
  2012-03-16  4:02 [ruby-core:43325] [ruby-trunk - Bug #6154][Open] Eliminate extending WaitReadable/Writable at runtime Charles Nutter
                   ` (7 preceding siblings ...)
  2013-03-15 17:24 ` [ruby-core:53449] " headius (Charles Nutter)
@ 2013-03-16  1:05 ` drbrain (Eric Hodel)
  2013-04-02 17:23 ` [ruby-core:53898] " headius (Charles Nutter)
                   ` (8 subsequent siblings)
  17 siblings, 0 replies; 25+ messages in thread
From: drbrain (Eric Hodel) @ 2013-03-16  1:05 UTC (permalink / raw
  To: ruby-core


Issue #6154 has been updated by drbrain (Eric Hodel).


Since a committer nominee was given permission to commit I think you should commit it
----------------------------------------
Feature #6154: Eliminate extending WaitReadable/Writable at runtime
https://bugs.ruby-lang.org/issues/6154#change-37656

Author: headius (Charles Nutter)
Status: Assigned
Priority: Normal
Assignee: matz (Yukihiro Matsumoto)
Category: 
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/

^ permalink raw reply	[flat|nested] 25+ messages in thread

* [ruby-core:53898] [ruby-trunk - Feature #6154] Eliminate extending WaitReadable/Writable at runtime
  2012-03-16  4:02 [ruby-core:43325] [ruby-trunk - Bug #6154][Open] Eliminate extending WaitReadable/Writable at runtime Charles Nutter
                   ` (8 preceding siblings ...)
  2013-03-16  1:05 ` [ruby-core:53474] " drbrain (Eric Hodel)
@ 2013-04-02 17:23 ` headius (Charles Nutter)
  2013-04-02 17:24 ` [ruby-core:53899] " headius (Charles Nutter)
                   ` (7 subsequent siblings)
  17 siblings, 0 replies; 25+ messages in thread
From: headius (Charles Nutter) @ 2013-04-02 17:23 UTC (permalink / raw
  To: ruby-core


Issue #6154 has been updated by headius (Charles Nutter).


Working on this today. I will post an updated patch for review before committing.
----------------------------------------
Feature #6154: Eliminate extending WaitReadable/Writable at runtime
https://bugs.ruby-lang.org/issues/6154#change-38115

Author: headius (Charles Nutter)
Status: Assigned
Priority: Normal
Assignee: matz (Yukihiro Matsumoto)
Category: 
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/

^ permalink raw reply	[flat|nested] 25+ messages in thread

* [ruby-core:53899] [ruby-trunk - Feature #6154] Eliminate extending WaitReadable/Writable at runtime
  2012-03-16  4:02 [ruby-core:43325] [ruby-trunk - Bug #6154][Open] Eliminate extending WaitReadable/Writable at runtime Charles Nutter
                   ` (9 preceding siblings ...)
  2013-04-02 17:23 ` [ruby-core:53898] " headius (Charles Nutter)
@ 2013-04-02 17:24 ` headius (Charles Nutter)
  2013-04-02 22:20 ` [ruby-core:53907] " headius (Charles Nutter)
                   ` (6 subsequent siblings)
  17 siblings, 0 replies; 25+ messages in thread
From: headius (Charles Nutter) @ 2013-04-02 17:24 UTC (permalink / raw
  To: ruby-core


Issue #6154 has been updated by headius (Charles Nutter).

Category set to core
Assignee changed from matz (Yukihiro Matsumoto) to headius (Charles Nutter)
% Done changed from 0 to 70


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

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/

^ permalink raw reply	[flat|nested] 25+ messages in thread

* [ruby-core:53907] [ruby-trunk - Feature #6154] Eliminate extending WaitReadable/Writable at runtime
  2012-03-16  4:02 [ruby-core:43325] [ruby-trunk - Bug #6154][Open] Eliminate extending WaitReadable/Writable at runtime Charles Nutter
                   ` (10 preceding siblings ...)
  2013-04-02 17:24 ` [ruby-core:53899] " headius (Charles Nutter)
@ 2013-04-02 22:20 ` headius (Charles Nutter)
  2013-04-02 23:51   ` [ruby-core:53911] " Eric Wong
  2013-04-02 22:37 ` [ruby-core:53908] " charliesome (Charlie Somerville)
                   ` (5 subsequent siblings)
  17 siblings, 1 reply; 25+ messages in thread
From: headius (Charles Nutter) @ 2013-04-02 22:20 UTC (permalink / raw
  To: ruby-core


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/

^ permalink raw reply	[flat|nested] 25+ messages in thread

* [ruby-core:53908] [ruby-trunk - Feature #6154] Eliminate extending WaitReadable/Writable at runtime
  2012-03-16  4:02 [ruby-core:43325] [ruby-trunk - Bug #6154][Open] Eliminate extending WaitReadable/Writable at runtime Charles Nutter
                   ` (11 preceding siblings ...)
  2013-04-02 22:20 ` [ruby-core:53907] " headius (Charles Nutter)
@ 2013-04-02 22:37 ` charliesome (Charlie Somerville)
  2013-04-02 22:46 ` [ruby-core:53909] " headius (Charles Nutter)
                   ` (4 subsequent siblings)
  17 siblings, 0 replies; 25+ messages in thread
From: charliesome (Charlie Somerville) @ 2013-04-02 22:37 UTC (permalink / raw
  To: ruby-core


Issue #6154 has been updated by charliesome (Charlie Somerville).


=begin
Crazy idea, but could you avoid introducing a new class by caching the iclass?

So instead of something like:

    /* when exception is raised */
    VALUE exc = ossl_exc_new(eSSLError, "read would block");
    rb_extend_object(exc, rb_mWaitWritable);
    rb_exc_raise(exc);

You might do something like this:

    VALUE cached_ssl_error_wait_writable_iclass;

    /* once off setup during vm boot */
    VALUE exc = ossl_exc_new(eSSLError, "read would block");
    rb_extend_object(exc, rb_mWaitWritable);
    cached_ssl_error_wait_writable_iclass = RBASIC(exc)->klass;
    
    /* when exception is raised */
    VALUE exc = ossl_exc_new(eSSLError, "read would block");
    RBASIC(exc)->klass = cached_ssl_error_wait_writable_iclass;
    rb_exc_raise(exc);

The benefit of this is that you don't have to change the behaviour that's visible from Ruby land.
=end
----------------------------------------
Feature #6154: Eliminate extending WaitReadable/Writable at runtime
https://bugs.ruby-lang.org/issues/6154#change-38124

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/

^ permalink raw reply	[flat|nested] 25+ messages in thread

* [ruby-core:53909] [ruby-trunk - Feature #6154] Eliminate extending WaitReadable/Writable at runtime
  2012-03-16  4:02 [ruby-core:43325] [ruby-trunk - Bug #6154][Open] Eliminate extending WaitReadable/Writable at runtime Charles Nutter
                   ` (12 preceding siblings ...)
  2013-04-02 22:37 ` [ruby-core:53908] " charliesome (Charlie Somerville)
@ 2013-04-02 22:46 ` headius (Charles Nutter)
  2013-04-02 22:47 ` [ruby-core:53910] " headius (Charles Nutter)
                   ` (3 subsequent siblings)
  17 siblings, 0 replies; 25+ messages in thread
From: headius (Charles Nutter) @ 2013-04-02 22:46 UTC (permalink / raw
  To: ruby-core


Issue #6154 has been updated by headius (Charles Nutter).


charliesome: Yeah, I was thinking about that as I created this patch.

It's possible, but it introduces a rather strange oddity: you'll have multiple exceptions floating around that look like singletons but are actually the same singleton class. If anyone adds methods to them it will add methods to all of them, but there's no way to know you're doing it. I believe this would mean you can't create proper isolated singleton instances/classes of these exception objects.

Are there other places in MRI where this pattern is used? If it's not being done elsewhere, I'd be reluctant to do it here.

The only behavior you have to change from Ruby land would be not testing for EAGAIN == exception. EAGAIN#===, WaitReadable#===, kind_of? and so on work just as they did before. I had to change the tests only because assert_raise does an == check internally (and it arguably should do === or kind_of? since that's closer to what rescue does).
----------------------------------------
Feature #6154: Eliminate extending WaitReadable/Writable at runtime
https://bugs.ruby-lang.org/issues/6154#change-38125

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/

^ permalink raw reply	[flat|nested] 25+ messages in thread

* [ruby-core:53910] [ruby-trunk - Feature #6154] Eliminate extending WaitReadable/Writable at runtime
  2012-03-16  4:02 [ruby-core:43325] [ruby-trunk - Bug #6154][Open] Eliminate extending WaitReadable/Writable at runtime Charles Nutter
                   ` (13 preceding siblings ...)
  2013-04-02 22:46 ` [ruby-core:53909] " headius (Charles Nutter)
@ 2013-04-02 22:47 ` headius (Charles Nutter)
  2013-04-03  1:23 ` [ruby-core:53918] " headius (Charles Nutter)
                   ` (2 subsequent siblings)
  17 siblings, 0 replies; 25+ messages in thread
From: headius (Charles Nutter) @ 2013-04-02 22:47 UTC (permalink / raw
  To: ruby-core


Issue #6154 has been updated by headius (Charles Nutter).


And by EAGAIN == exception I meant EAGAIN == exception.class, of course.
----------------------------------------
Feature #6154: Eliminate extending WaitReadable/Writable at runtime
https://bugs.ruby-lang.org/issues/6154#change-38126

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/

^ permalink raw reply	[flat|nested] 25+ messages in thread

* [ruby-core:53911] Re: [ruby-trunk - Feature #6154] Eliminate extending WaitReadable/Writable at runtime
  2013-04-02 22:20 ` [ruby-core:53907] " headius (Charles Nutter)
@ 2013-04-02 23:51   ` Eric Wong
  0 siblings, 0 replies; 25+ messages in thread
From: Eric Wong @ 2013-04-02 23:51 UTC (permalink / raw
  To: ruby-core

"headius (Charles Nutter)" <headius@headius.com> wrote:
> make test-all passes. Please review and comment.

> --- io.c	(revision 40073)
> +++ io.c	(working copy)
> @@ -133,6 +133,15 @@
> <snip>
> 
> +VALUE rb_eEAGAINWaitReadable;
> +VALUE rb_eEAGAINWaitWritable;
> +VALUE rb_eEWOULDBLOCKWaitReadable;
> +VALUE rb_eEWOULDBLOCKWaitWritable;
> +VALUE rb_eEINPROGRESSWaitWritable;
> +VALUE rb_eEINPROGRESSWaitReadable;

New globals introduced don't appear to be used outside of io.c,
so they can be made static (unless you intend to reference them
outside of io.c in the future)

> --- ext/openssl/ossl_ssl.c	(revision 40073)
> +++ ext/openssl/ossl_ssl.c	(working copy)
> @@ -26,6 +26,8 @@
>  
>  VALUE mSSL;
>  VALUE eSSLError;
> +VALUE eSSLErrorWaitReadable;
> +VALUE eSSLErrorWaitWritable;
>  VALUE cSSLContext;
>  VALUE cSSLSocket;

Likewise for these globals.

Otherwise, your patch looks good.  Thanks!

^ permalink raw reply	[flat|nested] 25+ messages in thread

* [ruby-core:53918] [ruby-trunk - Feature #6154] Eliminate extending WaitReadable/Writable at runtime
  2012-03-16  4:02 [ruby-core:43325] [ruby-trunk - Bug #6154][Open] Eliminate extending WaitReadable/Writable at runtime Charles Nutter
                   ` (14 preceding siblings ...)
  2013-04-02 22:47 ` [ruby-core:53910] " headius (Charles Nutter)
@ 2013-04-03  1:23 ` headius (Charles Nutter)
  2013-04-03  1:55 ` [ruby-core:53924] " headius (Charles Nutter)
  2013-04-04 23:58 ` [ruby-core:54004] " headius (Charles Nutter)
  17 siblings, 0 replies; 25+ messages in thread
From: headius (Charles Nutter) @ 2013-04-03  1:23 UTC (permalink / raw
  To: ruby-core


Issue #6154 has been updated by headius (Charles Nutter).


normalperson (Eric Wong) wrote:
>  > +VALUE rb_eEAGAINWaitReadable;
>  > +VALUE rb_eEAGAINWaitWritable;
>  > +VALUE rb_eEWOULDBLOCKWaitReadable;
>  > +VALUE rb_eEWOULDBLOCKWaitWritable;
>  > +VALUE rb_eEINPROGRESSWaitWritable;
>  > +VALUE rb_eEINPROGRESSWaitReadable;
>  
>  New globals introduced don't appear to be used outside of io.c,
>  so they can be made static (unless you intend to reference them
>  outside of io.c in the future)

Good call, will fix.
----------------------------------------
Feature #6154: Eliminate extending WaitReadable/Writable at runtime
https://bugs.ruby-lang.org/issues/6154#change-38133

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/

^ permalink raw reply	[flat|nested] 25+ messages in thread

* [ruby-core:53924] [ruby-trunk - Feature #6154] Eliminate extending WaitReadable/Writable at runtime
  2012-03-16  4:02 [ruby-core:43325] [ruby-trunk - Bug #6154][Open] Eliminate extending WaitReadable/Writable at runtime Charles Nutter
                   ` (15 preceding siblings ...)
  2013-04-03  1:23 ` [ruby-core:53918] " headius (Charles Nutter)
@ 2013-04-03  1:55 ` headius (Charles Nutter)
  2013-04-04 23:58 ` [ruby-core:54004] " headius (Charles Nutter)
  17 siblings, 0 replies; 25+ messages in thread
From: headius (Charles Nutter) @ 2013-04-03  1:55 UTC (permalink / raw
  To: ruby-core


Issue #6154 has been updated by headius (Charles Nutter).

File eagain_readwrite.diff added

Updated patch with new exception variables made static.
----------------------------------------
Feature #6154: Eliminate extending WaitReadable/Writable at runtime
https://bugs.ruby-lang.org/issues/6154#change-38136

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/

^ permalink raw reply	[flat|nested] 25+ messages in thread

* [ruby-core:54004] [ruby-trunk - Feature #6154] Eliminate extending WaitReadable/Writable at runtime
  2012-03-16  4:02 [ruby-core:43325] [ruby-trunk - Bug #6154][Open] Eliminate extending WaitReadable/Writable at runtime Charles Nutter
                   ` (16 preceding siblings ...)
  2013-04-03  1:55 ` [ruby-core:53924] " headius (Charles Nutter)
@ 2013-04-04 23:58 ` headius (Charles Nutter)
  17 siblings, 0 replies; 25+ messages in thread
From: headius (Charles Nutter) @ 2013-04-04 23:58 UTC (permalink / raw
  To: ruby-core


Issue #6154 has been updated by headius (Charles Nutter).


I am prepared to commit this fix if there's no other commentary.

A quick benchmark to show it in action: https://gist.github.com/headius/5315475

    $ ./ruby -I lib:ext:. nonblock_bench.rb 
      0.940000   0.170000   1.110000 (  1.113261)
      0.940000   0.170000   1.110000 (  1.104994)
      0.940000   0.160000   1.100000 (  1.108880)
      0.950000   0.170000   1.120000 (  1.107059)
      0.970000   0.170000   1.140000 (  1.148363)
    
    $ ./ruby -I lib:ext:.:ext/socket nonblock_bench.rb 
      0.660000   0.140000   0.800000 (  0.808189)
      0.680000   0.150000   0.830000 (  0.814997)
      0.670000   0.140000   0.810000 (  0.811901)
      0.670000   0.140000   0.810000 (  0.804447)
      0.670000   0.140000   0.810000 (  0.817231)
----------------------------------------
Feature #6154: Eliminate extending WaitReadable/Writable at runtime
https://bugs.ruby-lang.org/issues/6154#change-38238

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/

^ permalink raw reply	[flat|nested] 25+ messages in thread

end of thread, other threads:[~2013-04-05  0:21 UTC | newest]

Thread overview: 25+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2012-03-16  4:02 [ruby-core:43325] [ruby-trunk - Bug #6154][Open] Eliminate extending WaitReadable/Writable at runtime Charles Nutter
2012-03-16  4:05 ` [ruby-core:43326] [ruby-trunk - Bug #6154] " Charles Nutter
2012-03-17  0:24   ` [ruby-core:43361] " Eric Wong
2012-03-17  8:56     ` [ruby-core:43366] " Charles Oliver Nutter
2012-03-17 15:27 ` [ruby-core:43369] Re: [ruby-trunk - Bug #6154][Open] " Tanaka Akira
2012-03-17 22:33   ` [ruby-core:43377] " Charles Oliver Nutter
2012-03-18 15:16     ` [ruby-core:43453] " Hiroshi Nakamura
2012-03-22 17:37 ` [ruby-core:43560] [ruby-trunk - Bug #6154] " headius (Charles Nutter)
2012-03-22 18:38 ` [ruby-core:43562] " headius (Charles Nutter)
2012-11-16 16:27 ` [ruby-core:49437] [ruby-trunk - Feature " headius (Charles Nutter)
2012-11-18  4:44 ` [ruby-core:49517] " headius (Charles Nutter)
2012-11-24  4:51 ` [ruby-core:50011] " mame (Yusuke Endoh)
2012-12-12  0:15   ` [ruby-core:50798] " Eric Wong
2013-03-15 17:24 ` [ruby-core:53449] " headius (Charles Nutter)
2013-03-16  1:05 ` [ruby-core:53474] " drbrain (Eric Hodel)
2013-04-02 17:23 ` [ruby-core:53898] " headius (Charles Nutter)
2013-04-02 17:24 ` [ruby-core:53899] " headius (Charles Nutter)
2013-04-02 22:20 ` [ruby-core:53907] " headius (Charles Nutter)
2013-04-02 23:51   ` [ruby-core:53911] " Eric Wong
2013-04-02 22:37 ` [ruby-core:53908] " charliesome (Charlie Somerville)
2013-04-02 22:46 ` [ruby-core:53909] " headius (Charles Nutter)
2013-04-02 22:47 ` [ruby-core:53910] " headius (Charles Nutter)
2013-04-03  1:23 ` [ruby-core:53918] " headius (Charles Nutter)
2013-04-03  1:55 ` [ruby-core:53924] " headius (Charles Nutter)
2013-04-04 23:58 ` [ruby-core:54004] " headius (Charles Nutter)

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).