From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.3.2 (2011-06-06) on dcvr.yhbt.net X-Spam-Level: X-Spam-ASN: AS198093 171.25.193.0/24 X-Spam-Status: No, score=-2.1 required=3.0 tests=AWL,BAYES_00,RCVD_IN_XBL shortcircuit=no autolearn=no version=3.3.2 X-Original-To: spew@80x24.org Received: from 80x24.org (tor-exit7-readme.dfri.se [171.25.193.131]) by dcvr.yhbt.net (Postfix) with ESMTP id 48E70205E6 for ; Wed, 2 Dec 2015 02:06:56 +0000 (UTC) From: Eric Wong To: spew@80x24.org Subject: [REJECT] openssl: avoid kwarg parsing from C API at startup Date: Wed, 2 Dec 2015 02:06:54 +0000 Message-Id: <20151202020654.18328-1-e@80x24.org> List-Id: Avoiding kwarg parsing from the C API for accept_nonblock and connect_nonblock should reduce hash allocations and improve performance as it did for the regular socket variants. However, It's only a 1% speedup, so probably not worth it given the overheads of OpenSSL. It's also entirely possible I'm benchmarking this completely wrong because I'm not too familiar with OpenSSL. target 0: a (ruby 2.3.0dev (2015-12-02 trunk 52842) [x86_64-linux]) at "/home/ew/rrrr/b/ruby" target 1: b (ruby 2.3.0dev (2015-12-02 avoid-kwarg-capi 52842) [x86_64-linux]) at "/home/ew/ruby/b/ruby" ----------------------------------------------------------- ssl_startup require 'openssl' require 'socket' test_key_dh_1024 = OpenSSL::PKey::DH.new <<-_end_of_pem_ -----BEGIN DH PARAMETERS----- MIGHAoGBAKnKQ8MNK6nYZzLrrcuTsLxuiJGXoOO5gT+tljOTbHBuiktdMTITzIY0 pFxIvjG05D7HoBZQfrR0c92NGWPkAiCkhQKB8JCbPVzwNLDy6DZ0pmofDKrEsYHG AQjjxMXhwULlmuR/K+WwlaZPiLIBYalLAZQ7ZbOPeVkJ8ePao0eLAgEC -----END DH PARAMETERS----- _end_of_pem_ test_key_dh_1024.priv_key = OpenSSL::BN.new("48561834C67E65FFD2A9B47F41" \ "E5E78FDC95C387428FDB1E4B0188B64D1643C3A8D3455B945B7E8C4D166010C7C2" \ "CE23BFB9BEF43D0348FE7FA5284B0225E7FE1537546D114E3D8A4411B9B9351AB4" \ "51E1A358F50ED61B1F00DA29336EEBBD649980AC86D76AF8BBB065298C2052672E" \ "EF3EF13AB47A15275FC2836F3AC74CEA", 16) tmp_dh_callback = proc { test_key_dh_1024 } srv_ctx = OpenSSL::SSL::SSLContext.new srv_ctx.ciphers = 'ADH'.freeze srv_ctx.tmp_dh_callback = tmp_dh_callback require 'io/wait' require 'tmpdir' Dir.mktmpdir do |dir| path = "#{dir}/.sock" srv = UNIXServer.new(path) pid = fork do while c = srv.accept ssl = OpenSSL::SSL::SSLSocket.new(c, srv_ctx) case ssl.accept_nonblock(exception: false) when :wait_readable c.wait_readable when :wait_writable c.wait_writable else break end while true ssl.sync_close = true ssl.close end end buf = ''.b ctx = OpenSSL::SSL::SSLContext.new ctx.ciphers = 'ADH'.freeze 6000.times do c = UNIXSocket.new(path) ssl = OpenSSL::SSL::SSLSocket.new(c, ctx) case ssl.connect_nonblock(exception: false) when :wait_readable c.wait_readable when :wait_writable c.wait_writable else break end while true ssl.sync_close = true ssl.read(1, buf) ssl.close end Process.kill(:TERM, pid) Process.waitpid2(pid) end a 15.832569818012416 a 15.803461923962459 a 15.811633185017854 a 15.771200725110248 a 15.769214095082134 b 15.616948876995593 b 15.963800825877115 b 15.977134739980102 b 15.620980642037466 b 15.675114969955757 ----------------------------------------------------------- raw data: [["ssl_startup", [[15.832569818012416, 15.803461923962459, 15.811633185017854, 15.771200725110248, 15.769214095082134], [15.616948876995593, 15.963800825877115, 15.977134739980102, 15.620980642037466, 15.675114969955757]]]] Elapsed time: 157.843995469 (sec) ----------------------------------------------------------- benchmark results: minimum results in each 5 measurements. Execution time (sec) name a b ssl_startup 15.769 15.617 Speedup ratio: compare with the result of `a' (greater is better) name b ssl_startup 1.010 --- ext/openssl/lib/openssl/ssl.rb | 48 ++++++++++++++++++++ ext/openssl/ossl_ssl.c | 101 ++++++++++++++--------------------------- 2 files changed, 81 insertions(+), 68 deletions(-) diff --git a/ext/openssl/lib/openssl/ssl.rb b/ext/openssl/lib/openssl/ssl.rb index d3ae155..b273173 100644 --- a/ext/openssl/lib/openssl/ssl.rb +++ b/ext/openssl/lib/openssl/ssl.rb @@ -322,6 +322,54 @@ def session nil end + # call-seq: + # ssl.accept_nonblock([options]) => self + # + # Initiates the SSL/TLS handshake as a server in non-blocking manner. + # + # # emulates blocking accept + # begin + # ssl.accept_nonblock + # rescue IO::WaitReadable + # IO.select([s2]) + # retry + # rescue IO::WaitWritable + # IO.select(nil, [s2]) + # retry + # end + # + # By specifying `exception: false`, the options hash allows you to + # indicate that accept_nonblock should not raise an IO::WaitReadable or + # IO::WaitWritable exception, but return the symbol :wait_readable or + # :wait_writable instead. + def accept_nonblock(exception: true) + __accept_nonblock(exception) + end + + # call-seq: + # ssl.connect_nonblock([options]) => self + # + # Initiates the SSL/TLS handshake as a client in non-blocking manner. + # + # # emulates blocking connect + # begin + # ssl.connect_nonblock + # rescue IO::WaitReadable + # IO.select([s2]) + # retry + # rescue IO::WaitWritable + # IO.select(nil, [s2]) + # retry + # end + # + # By specifying `exception: false`, the options hash allows you to + # indicate that connect_nonblock should not raise an IO::WaitReadable or + # IO::WaitWritable exception, but return the symbol :wait_readable or + # :wait_writable instead. + def connect_nonblock(exception: true) + __connect_nonblock(exception) + end + private def using_anon_cipher? diff --git a/ext/openssl/ossl_ssl.c b/ext/openssl/ossl_ssl.c index 553eb14..5018dcb 100644 --- a/ext/openssl/ossl_ssl.c +++ b/ext/openssl/ossl_ssl.c @@ -1271,14 +1271,19 @@ no_exception_p(VALUE opts) return 0; } +/* + * nbex - + * * undef - blocking + * * true - non-blocking, raise exceptions on common errors + * * false - non-blocking, no exceptions on common errors + */ static VALUE -ossl_start_ssl(VALUE self, int (*func)(), const char *funcname, VALUE opts) +ossl_start_ssl(VALUE self, int (*func)(), const char *funcname, VALUE nbex) { SSL *ssl; rb_io_t *fptr; int ret, ret2; VALUE cb_state; - int nonblock = opts != Qfalse; rb_ivar_set(self, ID_callback_state, Qnil); @@ -1297,15 +1302,23 @@ ossl_start_ssl(VALUE self, int (*func)(), const char *funcname, VALUE opts) switch((ret2 = ssl_get_error(ssl, ret))){ case SSL_ERROR_WANT_WRITE: - if (no_exception_p(opts)) { return sym_wait_writable; } - write_would_block(nonblock); - rb_io_wait_writable(FPTR_TO_FD(fptr)); - continue; + switch (nbex) { + case Qfalse: return sym_wait_writable; + case Qundef: + rb_io_wait_writable(FPTR_TO_FD(fptr)); + continue; + default: + write_would_block(1); + } case SSL_ERROR_WANT_READ: - if (no_exception_p(opts)) { return sym_wait_readable; } - read_would_block(nonblock); - rb_io_wait_readable(FPTR_TO_FD(fptr)); - continue; + switch (nbex) { + case Qfalse: return sym_wait_readable; + case Qundef: + rb_io_wait_readable(FPTR_TO_FD(fptr)); + continue; + default: + read_would_block(1); + } case SSL_ERROR_SYSCALL: if (errno) rb_sys_fail(funcname); ossl_raise(eSSLError, "%s SYSCALL returned=%d errno=%d state=%s", funcname, ret2, errno, SSL_state_string_long(ssl)); @@ -1329,40 +1342,15 @@ ossl_ssl_connect(VALUE self) { ossl_ssl_setup(self); - return ossl_start_ssl(self, SSL_connect, "SSL_connect", Qfalse); + return ossl_start_ssl(self, SSL_connect, "SSL_connect", Qundef); } -/* - * call-seq: - * ssl.connect_nonblock([options]) => self - * - * Initiates the SSL/TLS handshake as a client in non-blocking manner. - * - * # emulates blocking connect - * begin - * ssl.connect_nonblock - * rescue IO::WaitReadable - * IO.select([s2]) - * retry - * rescue IO::WaitWritable - * IO.select(nil, [s2]) - * retry - * end - * - * By specifying `exception: false`, the options hash allows you to indicate - * that connect_nonblock should not raise an IO::WaitReadable or - * IO::WaitWritable exception, but return the symbol :wait_readable or - * :wait_writable instead. - */ static VALUE -ossl_ssl_connect_nonblock(int argc, VALUE *argv, VALUE self) +ossl_ssl_connect_nonblock(VALUE self, VALUE ex) { - VALUE opts; - rb_scan_args(argc, argv, "0:", &opts); - ossl_ssl_setup(self); - return ossl_start_ssl(self, SSL_connect, "SSL_connect", opts); + return ossl_start_ssl(self, SSL_connect, "SSL_connect", ex); } /* @@ -1377,40 +1365,15 @@ ossl_ssl_accept(VALUE self) { ossl_ssl_setup(self); - return ossl_start_ssl(self, SSL_accept, "SSL_accept", Qfalse); + return ossl_start_ssl(self, SSL_accept, "SSL_accept", Qundef); } -/* - * call-seq: - * ssl.accept_nonblock([options]) => self - * - * Initiates the SSL/TLS handshake as a server in non-blocking manner. - * - * # emulates blocking accept - * begin - * ssl.accept_nonblock - * rescue IO::WaitReadable - * IO.select([s2]) - * retry - * rescue IO::WaitWritable - * IO.select(nil, [s2]) - * retry - * end - * - * By specifying `exception: false`, the options hash allows you to indicate - * that accept_nonblock should not raise an IO::WaitReadable or - * IO::WaitWritable exception, but return the symbol :wait_readable or - * :wait_writable instead. - */ static VALUE -ossl_ssl_accept_nonblock(int argc, VALUE *argv, VALUE self) +ossl_ssl_accept_nonblock(VALUE self, VALUE ex) { - VALUE opts; - - rb_scan_args(argc, argv, "0:", &opts); ossl_ssl_setup(self); - return ossl_start_ssl(self, SSL_accept, "SSL_accept", opts); + return ossl_start_ssl(self, SSL_accept, "SSL_accept", ex); } static VALUE @@ -2287,9 +2250,11 @@ Init_ossl_ssl(void) rb_define_const(mSSLExtConfig, "OPENSSL_NO_SOCK", Qfalse); rb_define_alloc_func(cSSLSocket, ossl_ssl_s_alloc); rb_define_method(cSSLSocket, "connect", ossl_ssl_connect, 0); - rb_define_method(cSSLSocket, "connect_nonblock", ossl_ssl_connect_nonblock, -1); + rb_define_private_method(cSSLSocket, "__connect_nonblock", + ossl_ssl_connect_nonblock, 1); rb_define_method(cSSLSocket, "accept", ossl_ssl_accept, 0); - rb_define_method(cSSLSocket, "accept_nonblock", ossl_ssl_accept_nonblock, -1); + rb_define_private_method(cSSLSocket, "__accept_nonblock", + ossl_ssl_accept_nonblock, 1); rb_define_method(cSSLSocket, "sysread", ossl_ssl_read, -1); rb_define_private_method(cSSLSocket, "sysread_nonblock", ossl_ssl_read_nonblock, -1); rb_define_method(cSSLSocket, "syswrite", ossl_ssl_write, 1); -- EW