Rubyのaccept_nonblock(exception: false)

Rubyのサーバーソケットのaccept_nonblockは、IOread_nonblockと同様にexception: falseIO::WaitReadableを投げないようにできる。

例外を投げる場合と投げない場合で、どれくらい差があるものなのか実験してみた。

require 'benchmark'
require 'socket'

class Integer
  def to_comma
    to_s.reverse.scan(/\d\d?\d?/).join(',').reverse
  end
end

repeat_num = Integer(ARGV.shift || '1_000_000')
puts "repeat: #{repeat_num.to_comma}"

s = TCPServer.new(0)
Benchmark.bm(35) do |x|
  x.report('accept_nonblock') do
    repeat_num.times do
      begin
        s.accept_nonblock
      rescue IO::WaitReadable, Errno::EINTR
        # nothing to do.
      end
    end
  end

  x.report('accept_nonblock(exception: false)') do
    repeat_num.times do
      begin
        if (r = s.accept_nonblock(exception: false)) then
          if (r != :wait_readable) then
            r
          end
        end
      rescue Errno::EINTR
        # `exception: false'で捕捉されるのは
        # EAGAIN/EWOULDBLOCK,ECONNABORTE,EPROTOだけなので、例外EINTRの
        # 捕捉は必要。
      end
    end
  end
end

結果:

$ ./benchmark_socket_accept_nonblock.rb
repeat: 1,000,000
                                          user     system      total        real
accept_nonblock                      20.477151   7.877416  28.354567 ( 29.273192)
accept_nonblock(exception: false)     2.606960   6.471008   9.077968 (  9.108173)

exception: falseの方が3倍速い。 しかし差がつくのはそもそも接続が来ないときなんだよな。 使ったほうがよいのかどうか迷う。