diff options
author | normal <normal@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2015-11-12 02:00:41 +0000 |
---|---|---|
committer | normal <normal@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2015-11-12 02:00:41 +0000 |
commit | cee7f6911bf4e5e3b4874243e8fe3e2eb351f194 (patch) | |
tree | fa99e0e95d574f87c2c9a6e02c37bf6039f304e3 /prelude.rb | |
parent | 4a59524099fe7932430be19a8c5eb837a93e7594 (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.rb | 114 |
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 |