summaryrefslogtreecommitdiff
path: root/prelude.rb
diff options
context:
space:
mode:
authornormal <normal@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2015-11-12 02:00:41 +0000
committernormal <normal@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2015-11-12 02:00:41 +0000
commitcee7f6911bf4e5e3b4874243e8fe3e2eb351f194 (patch)
treefa99e0e95d574f87c2c9a6e02c37bf6039f304e3 /prelude.rb
parent4a59524099fe7932430be19a8c5eb837a93e7594 (diff)
io.c: avoid kwarg parsing in C API
* benchmark/bm_io_nonblock_noex2.rb: new benchmark based on bm_io_nonblock_noex.rb * io.c (io_read_nonblock): move documentation to prelude.rb (io_write_nonblock): ditto (Init_io): private, internal methods for prelude.rb use only * prelude.rb (IO#read_nonblock): wrapper + documentation (IO#write_nonblock): ditto [ruby-core:71439] [Feature #11339] rb_scan_args and hash lookups for kwargs in the C API are clumsy and slow. Instead of improving the C API for performance, use Ruby instead :) Implement IO#read_nonblock and IO#write_nonblock in prelude.rb to avoid argument parsing via rb_scan_args and hash lookups. This speeds up IO#write_nonblock and IO#read_nonblock benchmarks in both cases, including the original non-idiomatic case where the `exception: false' hash is pre-allocated to avoid GC pressure. Now, writing the kwargs in natural, idiomatic Ruby is fastest. I've added the noex2 benchmark to show this. 2015-11-12 01:41:12 +0000 target 0: a (ruby 2.3.0dev (2015-11-11 trunk 52540) [x86_64-linux]) target 1: b (ruby 2.3.0dev (2015-11-11 avoid-kwarg-capi 52540) ----------------------------------------------------------- benchmark results: minimum results in each 10 measurements. Execution time (sec) name a b io_nonblock_noex 2.508 2.382 io_nonblock_noex2 2.950 1.882 Speedup ratio: compare with the result of `a' (greater is better) name b io_nonblock_noex 1.053 io_nonblock_noex2 1.567 git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@52541 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'prelude.rb')
-rw-r--r--prelude.rb114
1 files changed, 114 insertions, 0 deletions
diff --git a/prelude.rb b/prelude.rb
index cc24a81474..20a9e2df10 100644
--- a/prelude.rb
+++ b/prelude.rb
@@ -13,3 +13,117 @@ class Thread
}
end
end
+
+class IO
+
+ # call-seq:
+ # ios.read_nonblock(maxlen) -> string
+ # ios.read_nonblock(maxlen, outbuf) -> outbuf
+ #
+ # Reads at most <i>maxlen</i> bytes from <em>ios</em> using
+ # the read(2) system call after O_NONBLOCK is set for
+ # the underlying file descriptor.
+ #
+ # If the optional <i>outbuf</i> argument is present,
+ # it must reference a String, which will receive the data.
+ # The <i>outbuf</i> will contain only the received data after the method call
+ # even if it is not empty at the beginning.
+ #
+ # read_nonblock just calls the read(2) system call.
+ # It causes all errors the read(2) system call causes: Errno::EWOULDBLOCK, Errno::EINTR, etc.
+ # The caller should care such errors.
+ #
+ # If the exception is Errno::EWOULDBLOCK or Errno::EAGAIN,
+ # it is extended by IO::WaitReadable.
+ # So IO::WaitReadable can be used to rescue the exceptions for retrying
+ # read_nonblock.
+ #
+ # read_nonblock causes EOFError on EOF.
+ #
+ # If the read byte buffer is not empty,
+ # read_nonblock reads from the buffer like readpartial.
+ # In this case, the read(2) system call is not called.
+ #
+ # When read_nonblock raises an exception kind of IO::WaitReadable,
+ # read_nonblock should not be called
+ # until io is readable for avoiding busy loop.
+ # This can be done as follows.
+ #
+ # # emulates blocking read (readpartial).
+ # begin
+ # result = io.read_nonblock(maxlen)
+ # rescue IO::WaitReadable
+ # IO.select([io])
+ # retry
+ # end
+ #
+ # Although IO#read_nonblock doesn't raise IO::WaitWritable.
+ # OpenSSL::Buffering#read_nonblock can raise IO::WaitWritable.
+ # If IO and SSL should be used polymorphically,
+ # IO::WaitWritable should be rescued too.
+ # See the document of OpenSSL::Buffering#read_nonblock for sample code.
+ #
+ # Note that this method is identical to readpartial
+ # except the non-blocking flag is set.
+ def read_nonblock(len, buf = nil, exception: true)
+ __read_nonblock(len, buf, exception)
+ end
+
+ # call-seq:
+ # ios.write_nonblock(string) -> integer
+ # ios.write_nonblock(string [, options]) -> integer
+ #
+ # Writes the given string to <em>ios</em> using
+ # the write(2) system call after O_NONBLOCK is set for
+ # the underlying file descriptor.
+ #
+ # It returns the number of bytes written.
+ #
+ # write_nonblock just calls the write(2) system call.
+ # It causes all errors the write(2) system call causes: Errno::EWOULDBLOCK, Errno::EINTR, etc.
+ # The result may also be smaller than string.length (partial write).
+ # The caller should care such errors and partial write.
+ #
+ # If the exception is Errno::EWOULDBLOCK or Errno::EAGAIN,
+ # it is extended by IO::WaitWritable.
+ # So IO::WaitWritable can be used to rescue the exceptions for retrying write_nonblock.
+ #
+ # # Creates a pipe.
+ # r, w = IO.pipe
+ #
+ # # write_nonblock writes only 65536 bytes and return 65536.
+ # # (The pipe size is 65536 bytes on this environment.)
+ # s = "a" #100000
+ # p w.write_nonblock(s) #=> 65536
+ #
+ # # write_nonblock cannot write a byte and raise EWOULDBLOCK (EAGAIN).
+ # p w.write_nonblock("b") # Resource temporarily unavailable (Errno::EAGAIN)
+ #
+ # If the write buffer is not empty, it is flushed at first.
+ #
+ # When write_nonblock raises an exception kind of IO::WaitWritable,
+ # write_nonblock should not be called
+ # until io is writable for avoiding busy loop.
+ # This can be done as follows.
+ #
+ # begin
+ # result = io.write_nonblock(string)
+ # rescue IO::WaitWritable, Errno::EINTR
+ # IO.select(nil, [io])
+ # retry
+ # end
+ #
+ # Note that this doesn't guarantee to write all data in string.
+ # The length written is reported as result and it should be checked later.
+ #
+ # On some platforms such as Windows, write_nonblock is not supported
+ # according to the kind of the IO object.
+ # In such cases, write_nonblock raises <code>Errno::EBADF</code>.
+ #
+ # By specifying `exception: false`, the options hash allows you to indicate
+ # that write_nonblock should not raise an IO::WaitWritable exception, but
+ # return the symbol :wait_writable instead.
+ def write_nonblock(buf, exception: true)
+ __write_nonblock(buf, exception)
+ end
+end