From: Eric Wong Date: 2011-06-09T17:52:26+09:00 Subject: [ruby-core:36879] Re: [Ruby 1.9 - Feature #3905] rb_clear_cache_by_class() called often during GC for non-blocking I/O Charles Oliver Nutter wrote: > Interested in the overhead of this EAGAIN nonsense, I ran a quick > benchmark. I include it here for the amusement of all. It demonstrates > pretty clearly the impact of the extend(WaitReadable), since that's > really the only thing that differs between the two (at least in > JRuby). Since you provided the benchmark code, I reformatted and made a version of it for kgio (see below). Summary: ephemeral-class performance noticeably (and will have a bigger impact for bigger applications, not small benchmark scripts) kgio reduces overhead greatly by avoiding exceptions. Real-world results (see the dalli README) are less impressive, of course, but still noticeable. Results below: == Ruby trunk ruby 1.9.3dev (2011-06-09 trunk 31961) [x86_64-linux] 0.150000 0.000000 0.150000 ( 0.158108) 0.150000 0.000000 0.150000 ( 0.156812) 0.150000 0.000000 0.150000 ( 0.157045) 0.150000 0.000000 0.150000 ( 0.156393) 0.150000 0.000000 0.150000 ( 0.156002) 0.150000 0.000000 0.150000 ( 0.159343) 0.150000 0.010000 0.160000 ( 0.159755) 0.150000 0.000000 0.150000 ( 0.158942) 0.150000 0.000000 0.150000 ( 0.158270) 0.150000 0.000000 0.150000 ( 0.158734) == git clone git://bogomips.org/ruby.git ephemeral-class ruby 1.9.3dev (2011-06-09 trunk 31961) [x86_64-linux] loop_eagain (read_nonblock) 0.100000 0.000000 0.100000 ( 0.101462) 0.080000 0.010000 0.090000 ( 0.100878) 0.100000 0.000000 0.100000 ( 0.100596) 0.100000 0.000000 0.100000 ( 0.101341) 0.100000 0.000000 0.100000 ( 0.100753) 0.100000 0.000000 0.100000 ( 0.099882) 0.090000 0.000000 0.090000 ( 0.100205) 0.100000 0.000000 0.100000 ( 0.099895) 0.100000 0.000000 0.100000 ( 0.101218) 0.090000 0.010000 0.100000 ( 0.100429) loop_wait_readable (Kgio::Socket#kgio_tryread) 0.000000 0.010000 0.010000 ( 0.004570) 0.010000 0.000000 0.010000 ( 0.005165) 0.010000 0.000000 0.010000 ( 0.004236) 0.000000 0.000000 0.000000 ( 0.004767) 0.000000 0.000000 0.000000 ( 0.004186) 0.000000 0.000000 0.000000 ( 0.004813) 0.000000 0.000000 0.000000 ( 0.004186) 0.000000 0.000000 0.000000 ( 0.004755) 0.000000 0.000000 0.000000 ( 0.004168) 0.000000 0.000000 0.000000 ( 0.004199) loop_wait_readable (Kgio::Pipe#kgio_tryread) 0.000000 0.000000 0.000000 ( 0.005383) 0.000000 0.000000 0.000000 ( 0.004787) 0.010000 0.000000 0.010000 ( 0.005284) 0.000000 0.000000 0.000000 ( 0.004784) 0.010000 0.000000 0.010000 ( 0.005311) 0.000000 0.000000 0.000000 ( 0.004770) 0.010000 0.000000 0.010000 ( 0.005299) 0.000000 0.000000 0.000000 ( 0.004811) 0.000000 0.000000 0.000000 ( 0.005347) 0.000000 0.000000 0.000000 ( 0.004770) I added a separate set of tests for Kgio::Pipe vs Kgio::Socket since Kgio::Socket has a small, Linux-only optimization to avoid fcntl() syscalls entirely. # script I used: # based on one liner by Charles Nutter [ruby-core:36875] # reformatted and added kgio tests ----------------------- 8< ------------------------ require 'kgio' require 'benchmark' def loop_eagain(sock) i = 0 begin sock.read_nonblock(1) rescue Errno::EAGAIN return if i >= 10_000 i += 1 retry end end def loop_wait_readable(sock) i = 0 case sock.kgio_tryread(1) when :wait_readable return if i >= 10_000 i += 1 when String then break # success when nil then break # EOF end while true end host = 'yhbt.net' # yhbt.net webmaster is OK with testing against it puts "loop_eagain (read_nonblock)" 10.times { sock = TCPSocket.new(host, 80) puts Benchmark.measure { loop_eagain(sock) } } puts "loop_wait_readable (Kgio::Socket#kgio_tryread)" addr = Socket.pack_sockaddr_in(80, host) # kgio doesn't do DNS lookups 10.times { sock = Kgio::Socket.new(addr) puts Benchmark.measure { loop_wait_readable(sock) } } puts "loop_wait_readable (Kgio::Pipe#kgio_tryread)" addr = Socket.pack_sockaddr_in(80, host) # kgio doesn't do DNS lookups 10.times { r, w = Kgio::Pipe.new puts Benchmark.measure { loop_wait_readable(r) } } ------------------------- 8< ------------------------ -- Eric Wong