Project

General

Profile

« Previous | Next » 

Revision d65d2fb6

Added by ko1 (Koichi Sasada) over 1 year ago

Do not poll first

Before this patch, the MN scheduler waits for the IO with the
following steps:

  1. poll(fd, timeout=0) to check fd is ready or not.
  2. if fd is not ready, waits with MN thread scheduler
  3. call func to issue the blocking I/O call

The advantage of advanced poll() is we can wait for the
IO ready for any fds. However poll() becomes overhead
for already ready fds.

This patch changes the steps like:

  1. call func to issue the blocking I/O call
  2. if the func returns EWOULDBLOCK the fd is O_NONBLOCK
    and we need to wait for fd is ready so that waits with MN
    thread scheduler.

In this case, we can wait only for O_NONBLOCK fds. Otherwise
it waits with blocking operations such as read() system call.
However we don't need to call poll() to check fd is ready
in advance.

With this patch we can observe performance improvement
on microbenchmark which repeats blocking I/O (not
O_NONBLOCK fd) with and without MN thread scheduler.

require 'benchmark'

f = open('/dev/null', 'w')
f.sync = true

TN = 1
N = 1_000_000 / TN

Benchmark.bm{|x|
  x.report{
    TN.times.map{
      Thread.new{
        N.times{f.print '.'}
      }
    }.each(&:join)
  }
}
__END__
TN = 1
                 user     system      total        real
ruby32       0.393966   0.101122   0.495088 (  0.495235)
ruby33       0.493963   0.089521   0.583484 (  0.584091)
ruby33+MN    0.639333   0.200843   0.840176 (  0.840291) <- Slow
this+MN      0.512231   0.099091   0.611322 (  0.611074) <- Good