Rainbows! Rack HTTP server user/dev discussion
 help / Atom feed
* [PATCH 0/5] a few more odds and ends before 5.0
@ 2015-11-21  8:52 Eric Wong
  2015-11-21  8:52 ` [PATCH 1/5] tiny bytecode reductions for cold paths Eric Wong
                   ` (4 more replies)
  0 siblings, 5 replies; 6+ messages in thread
From: Eric Wong @ 2015-11-21  8:52 UTC (permalink / raw)
  To: rainbows-public

A few more odds and ends before 5.0 (sometime next week).

I'll do some more testing later, but overall keeping this up-to-date
with the latest developments in unicorn isn't as bad as I thought it'd
be.

5 more changes:
      tiny bytecode reductions for cold paths
      Ruby 1.9.3+-only cleanups
      response: avoid garbage string entirely
      revactor: remove fcntl dependency
      response: simplify regexp

 bin/rainbows                               |  6 +++---
 lib/rainbows.rb                            |  5 ++---
 lib/rainbows/configurator.rb               | 16 ++++++++--------
 lib/rainbows/coolio.rb                     |  9 ++-------
 lib/rainbows/coolio/heartbeat.rb           |  2 +-
 lib/rainbows/epoll/client.rb               |  5 ++---
 lib/rainbows/fiber/base.rb                 |  2 +-
 lib/rainbows/http_server.rb                |  4 ++--
 lib/rainbows/response.rb                   | 10 +++++-----
 lib/rainbows/revactor.rb                   |  1 -
 lib/rainbows/revactor/client.rb            |  3 +--
 lib/rainbows/stream_response_epoll.rb      |  2 +-
 lib/rainbows/writer_thread_pool.rb         |  4 ++--
 lib/rainbows/xepoll_thread_pool/client.rb  |  3 +--
 lib/rainbows/xepoll_thread_spawn/client.rb |  3 +--
 15 files changed, 32 insertions(+), 43 deletions(-)


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH 1/5] tiny bytecode reductions for cold paths
  2015-11-21  8:52 [PATCH 0/5] a few more odds and ends before 5.0 Eric Wong
@ 2015-11-21  8:52 ` Eric Wong
  2015-11-21  8:52 ` [PATCH 2/5] Ruby 1.9.3+-only cleanups Eric Wong
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: Eric Wong @ 2015-11-21  8:52 UTC (permalink / raw)
  To: rainbows-public

Less code in cold paths can improve speed for hot paths.

Single-byte strings for String#split is optimized in mainline Ruby,
so it's not actually a performance loss for sendfile_range in
response.rb

Regexps are at least 400 bytes each, so prefer non-Regexps
if possible, especially for cold sites where performance does
not matter.
---
 lib/rainbows/http_server.rb           | 4 ++--
 lib/rainbows/response.rb              | 4 ++--
 lib/rainbows/stream_response_epoll.rb | 2 +-
 3 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/lib/rainbows/http_server.rb b/lib/rainbows/http_server.rb
index 429539c..33bb96c 100644
--- a/lib/rainbows/http_server.rb
+++ b/lib/rainbows/http_server.rb
@@ -82,10 +82,10 @@ def svc
   end
 
   def use=(mod)
-    @use = mod.to_s.split(/::/)[-1].to_sym
+    @use = mod.to_s.split('::')[-1].to_sym
     new_defaults = {
       'rainbows.model' => @use,
-      'rack.multithread' => !!(mod.to_s =~ /Thread/),
+      'rack.multithread' => mod.to_s.include?('Thread'),
       'rainbows.autochunk' => [:Coolio,:Rev,:Epoll,:XEpoll,
                                :EventMachine,:NeverBlock].include?(@use),
     }
diff --git a/lib/rainbows/response.rb b/lib/rainbows/response.rb
index 0b5e542..9bbea81 100644
--- a/lib/rainbows/response.rb
+++ b/lib/rainbows/response.rb
@@ -40,7 +40,7 @@ def write_headers(status, headers, alive, body)
       else
         if /\n/ =~ value
           # avoiding blank, key-only cookies with /\n+/
-          buf << value.split(/\n+/).map! { |v| "#{key}: #{v}\r\n" }.join
+          value.split(/\n+/).each { |v| buf << "#{key}: #{v}\r\n" }
         else
           buf << "#{key}: #{value}\r\n"
         end
@@ -151,7 +151,7 @@ def sendfile_range(status, headers)
       200 == status &&
       /\Abytes=(\d+-\d*|\d*-\d+)\z/ =~ @hp.env['HTTP_RANGE'] or
         return
-      a, b = $1.split(/-/)
+      a, b = $1.split('-'.freeze)
 
       # HeaderHash is quite expensive, and Rack::File currently
       # uses a regular Ruby Hash with properly-cased headers the
diff --git a/lib/rainbows/stream_response_epoll.rb b/lib/rainbows/stream_response_epoll.rb
index 7c2f4f6..48ef298 100644
--- a/lib/rainbows/stream_response_epoll.rb
+++ b/lib/rainbows/stream_response_epoll.rb
@@ -39,7 +39,7 @@ def http_response_write(socket, status, headers, body)
         else
           if /\n/ =~ value
             # avoiding blank, key-only cookies with /\n+/
-            buf << value.split(/\n+/).map! { |v| "#{key}: #{v}\r\n" }.join
+            value.split(/\n+/).each { |v| buf << "#{key}: #{v}\r\n" }
           else
             buf << "#{key}: #{value}\r\n"
           end
-- 
EW


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH 2/5] Ruby 1.9.3+-only cleanups
  2015-11-21  8:52 [PATCH 0/5] a few more odds and ends before 5.0 Eric Wong
  2015-11-21  8:52 ` [PATCH 1/5] tiny bytecode reductions for cold paths Eric Wong
@ 2015-11-21  8:52 ` Eric Wong
  2015-11-21  8:52 ` [PATCH 3/5] response: avoid garbage string entirely Eric Wong
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: Eric Wong @ 2015-11-21  8:52 UTC (permalink / raw)
  To: rainbows-public

unicorn 5 will only support Ruby 1.9.3 and later, so remove
some checks for Hash#compare_by_identity and IO.copy_stream
which we know exist in Ruby 1.9.

Favor &:sym proc dispatch to avoid unnecessary captures and
bytecode size increases, too.

Finally, ensure we fail fast by converting some literal
hashes to use non-arrow syntax for symbolic keys.
---
 bin/rainbows                               |  6 +++---
 lib/rainbows.rb                            |  5 ++---
 lib/rainbows/configurator.rb               | 16 ++++++++--------
 lib/rainbows/coolio.rb                     |  9 ++-------
 lib/rainbows/coolio/heartbeat.rb           |  2 +-
 lib/rainbows/epoll/client.rb               |  5 ++---
 lib/rainbows/fiber/base.rb                 |  2 +-
 lib/rainbows/writer_thread_pool.rb         |  4 ++--
 lib/rainbows/xepoll_thread_pool/client.rb  |  3 +--
 lib/rainbows/xepoll_thread_spawn/client.rb |  3 +--
 10 files changed, 23 insertions(+), 32 deletions(-)

diff --git a/bin/rainbows b/bin/rainbows
index f5ddaa7..c659afa 100755
--- a/bin/rainbows
+++ b/bin/rainbows
@@ -117,9 +117,9 @@
 if $DEBUG
   require 'pp'
   pp({
-    :unicorn_options => options,
-    :app => app,
-    :daemonize => rackup_opts[:daemonize],
+    unicorn_options: options,
+    app: app,
+    daemonize: rackup_opts[:daemonize],
   })
 end
 
diff --git a/lib/rainbows.rb b/lib/rainbows.rb
index f23b387..6e7e4f2 100644
--- a/lib/rainbows.rb
+++ b/lib/rainbows.rb
@@ -11,8 +11,7 @@ module Rainbows
 
   # map of numeric file descriptors to IO objects to avoid using IO.new
   # and potentially causing race conditions when using /dev/fd/
-  FD_MAP = {}
-  FD_MAP.compare_by_identity if FD_MAP.respond_to?(:compare_by_identity)
+  FD_MAP = {}.compare_by_identity
 
   require 'rainbows/const'
   require 'rainbows/http_parser'
@@ -92,7 +91,7 @@ def self.quit!
       tmp = @readers.dup
       @readers.clear
       tmp.each { |s| s.close rescue nil }.clear
-      @at_quit.each { |task| task.call }
+      @at_quit.each(&:call)
 
       # XXX hack to break out of IO.select in worker_loop for some models
       Process.kill(:QUIT, $$)
diff --git a/lib/rainbows/configurator.rb b/lib/rainbows/configurator.rb
index 92dacd6..73820a1 100644
--- a/lib/rainbows/configurator.rb
+++ b/lib/rainbows/configurator.rb
@@ -21,14 +21,14 @@
 #   stdout_path "/path/to/output.log"
 module Rainbows::Configurator
   Unicorn::Configurator::DEFAULTS.merge!({
-    :use => Rainbows::Base,
-    :worker_connections => 50,
-    :keepalive_timeout => 5,
-    :keepalive_requests => 100,
-    :client_max_body_size => 1024 * 1024,
-    :client_header_buffer_size => 1024,
-    :client_max_header_size => 112 * 1024,
-    :copy_stream => IO.respond_to?(:copy_stream) ? IO : false,
+    use: Rainbows::Base,
+    worker_connections: 50,
+    keepalive_timeout: 5,
+    keepalive_requests: 100,
+    client_max_body_size: 1024 * 1024,
+    client_header_buffer_size: 1024,
+    client_max_header_size: 112 * 1024,
+    copy_stream: IO,
   })
 
   # Configures \Rainbows! with a given concurrency model to +use+ and
diff --git a/lib/rainbows/coolio.rb b/lib/rainbows/coolio.rb
index a993060..2aba3ea 100644
--- a/lib/rainbows/coolio.rb
+++ b/lib/rainbows/coolio.rb
@@ -27,15 +27,10 @@
 module Rainbows::Coolio
   # :stopdoc:
   # keep-alive timeout scoreboard
-  KATO = {}
+  KATO = {}.compare_by_identity
 
   # all connected clients
-  CONN = {}
-
-  if {}.respond_to?(:compare_by_identity)
-    CONN.compare_by_identity
-    KATO.compare_by_identity
-  end
+  CONN = {}.compare_by_identity
 
   autoload :Client, 'rainbows/coolio/client'
   autoload :Master, 'rainbows/coolio/master'
diff --git a/lib/rainbows/coolio/heartbeat.rb b/lib/rainbows/coolio/heartbeat.rb
index fcfbb0f..adea248 100644
--- a/lib/rainbows/coolio/heartbeat.rb
+++ b/lib/rainbows/coolio/heartbeat.rb
@@ -9,7 +9,7 @@ class Rainbows::Coolio::Heartbeat < Coolio::TimerWatcher
   KATO = Rainbows::Coolio::KATO
   CONN = Rainbows::Coolio::CONN
   Rainbows.config!(self, :keepalive_timeout)
-  Rainbows.at_quit { KATO.each_key { |client| client.timeout? }.clear }
+  Rainbows.at_quit { KATO.each_key(&:timeout?).clear }
 
   def on_timer
     if (ot = KEEPALIVE_TIMEOUT) >= 0
diff --git a/lib/rainbows/epoll/client.rb b/lib/rainbows/epoll/client.rb
index fe04258..85e504c 100644
--- a/lib/rainbows/epoll/client.rb
+++ b/lib/rainbows/epoll/client.rb
@@ -9,9 +9,8 @@ module Rainbows::Epoll::Client
   IN = SleepyPenguin::Epoll::IN | SleepyPenguin::Epoll::ONESHOT
   OUT = SleepyPenguin::Epoll::OUT | SleepyPenguin::Epoll::ONESHOT
   EPINOUT = IN | OUT
-  KATO = {}
-  KATO.compare_by_identity if KATO.respond_to?(:compare_by_identity)
-  Rainbows.at_quit { KATO.each_key { |k| k.timeout! }.clear }
+  KATO = {}.compare_by_identity
+  Rainbows.at_quit { KATO.each_key(&:timeout!).clear }
   Rainbows.config!(self, :keepalive_timeout)
   EP = Rainbows::EP
   @@last_expire = Rainbows.now
diff --git a/lib/rainbows/fiber/base.rb b/lib/rainbows/fiber/base.rb
index 7c4fb59..a3c098a 100644
--- a/lib/rainbows/fiber/base.rb
+++ b/lib/rainbows/fiber/base.rb
@@ -50,7 +50,7 @@ def schedule_sleepers
         false
       end
     }
-    fibs.each { |fib| fib.resume }
+    fibs.each(&:resume)
 
     max_sleep = 1.0 # wake up semi-frequently to prevent SIGKILL from master
     if max
diff --git a/lib/rainbows/writer_thread_pool.rb b/lib/rainbows/writer_thread_pool.rb
index b5688e0..657d076 100644
--- a/lib/rainbows/writer_thread_pool.rb
+++ b/lib/rainbows/writer_thread_pool.rb
@@ -50,9 +50,9 @@ def worker_loop(worker) # :nodoc:
       end
     end
 
-    @@q = qp.map { |q| q.queue }
+    @@q = qp.map(&:queue)
     super(worker) # accept loop from Unicorn
-    qp.each { |q| q.quit! }
+    qp.each(&:quit!)
   end
   # :startdoc:
 end
diff --git a/lib/rainbows/xepoll_thread_pool/client.rb b/lib/rainbows/xepoll_thread_pool/client.rb
index 760bbde..ca62727 100644
--- a/lib/rainbows/xepoll_thread_pool/client.rb
+++ b/lib/rainbows/xepoll_thread_pool/client.rb
@@ -38,8 +38,7 @@ def self.app_run(queue)
   ep = SleepyPenguin::Epoll
   EP = ep.new
   IN = ep::IN | ep::ONESHOT
-  KATO = {}
-  KATO.compare_by_identity if KATO.respond_to?(:compare_by_identity)
+  KATO = {}.compare_by_identity
   LOCK = Mutex.new
   Rainbows.at_quit do
     clients = nil
diff --git a/lib/rainbows/xepoll_thread_spawn/client.rb b/lib/rainbows/xepoll_thread_spawn/client.rb
index 67c5976..218db3e 100644
--- a/lib/rainbows/xepoll_thread_spawn/client.rb
+++ b/lib/rainbows/xepoll_thread_spawn/client.rb
@@ -27,8 +27,7 @@ def self.included(klass) # included in Rainbows::Client
   ep = SleepyPenguin::Epoll
   EP = ep.new
   IN = ep::IN | ep::ONESHOT
-  KATO = {}
-  KATO.compare_by_identity if KATO.respond_to?(:compare_by_identity)
+  KATO = {}.compare_by_identity
   LOCK = Mutex.new
   Rainbows.at_quit do
     clients = nil
-- 
EW


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH 3/5] response: avoid garbage string entirely
  2015-11-21  8:52 [PATCH 0/5] a few more odds and ends before 5.0 Eric Wong
  2015-11-21  8:52 ` [PATCH 1/5] tiny bytecode reductions for cold paths Eric Wong
  2015-11-21  8:52 ` [PATCH 2/5] Ruby 1.9.3+-only cleanups Eric Wong
@ 2015-11-21  8:52 ` Eric Wong
  2015-11-21  8:52 ` [PATCH 4/5] revactor: remove fcntl dependency Eric Wong
  2015-11-21  8:52 ` [PATCH 5/5] response: simplify regexp Eric Wong
  4 siblings, 0 replies; 6+ messages in thread
From: Eric Wong @ 2015-11-21  8:52 UTC (permalink / raw)
  To: rainbows-public

Even in frozen string literals enabled in Ruby 2.3.0dev,
dstrings still create garbage as the optimizer is
not yet smart enough to optimize it despite the limited
choice of internals being known.

Maybe in the future Ruby will be smart enough, but not yet...
---
 lib/rainbows/response.rb | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/lib/rainbows/response.rb b/lib/rainbows/response.rb
index 9bbea81..ac50321 100644
--- a/lib/rainbows/response.rb
+++ b/lib/rainbows/response.rb
@@ -46,8 +46,8 @@ def write_headers(status, headers, alive, body)
         end
       end
     end
-    write(buf << "Connection: #{alive ? 'keep-alive'.freeze
-                                      : 'close'.freeze}\r\n\r\n")
+    write(buf << (alive ? "Connection: keep-alive\r\n\r\n".freeze
+                        : "Connection: close\r\n\r\n".freeze))
 
     if hijack
       body = nil # ensure caller does not close body
-- 
EW


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH 4/5] revactor: remove fcntl dependency
  2015-11-21  8:52 [PATCH 0/5] a few more odds and ends before 5.0 Eric Wong
                   ` (2 preceding siblings ...)
  2015-11-21  8:52 ` [PATCH 3/5] response: avoid garbage string entirely Eric Wong
@ 2015-11-21  8:52 ` Eric Wong
  2015-11-21  8:52 ` [PATCH 5/5] response: simplify regexp Eric Wong
  4 siblings, 0 replies; 6+ messages in thread
From: Eric Wong @ 2015-11-21  8:52 UTC (permalink / raw)
  To: rainbows-public

In the unlikely case somebody runs revactor, they won't need to
load the extra fcntl.so library into their process anymore.

In retrospect, we could've alway used IO#close_on_exec= since
it appeared in 1.9.1 and (IIRC) revactor always required 1.9.1+
---
 lib/rainbows/revactor.rb        | 1 -
 lib/rainbows/revactor/client.rb | 3 +--
 2 files changed, 1 insertion(+), 3 deletions(-)

diff --git a/lib/rainbows/revactor.rb b/lib/rainbows/revactor.rb
index e68fee6..fae5f16 100644
--- a/lib/rainbows/revactor.rb
+++ b/lib/rainbows/revactor.rb
@@ -1,6 +1,5 @@
 # -*- encoding: binary -*-
 require 'revactor'
-require 'fcntl'
 Revactor::VERSION >= '0.1.5' or abort 'revactor 0.1.5 is required'
 
 # Enables use of the Actor model through {Revactor}[http://revactor.org]
diff --git a/lib/rainbows/revactor/client.rb b/lib/rainbows/revactor/client.rb
index c1cb7aa..5b1e52d 100644
--- a/lib/rainbows/revactor/client.rb
+++ b/lib/rainbows/revactor/client.rb
@@ -1,6 +1,5 @@
 # -*- encoding: binary -*-
 # :enddoc:
-require 'fcntl'
 class Rainbows::Revactor::Client
   autoload :TeeSocket, 'rainbows/revactor/client/tee_socket'
   RD_ARGS = {}
@@ -11,7 +10,7 @@ class Rainbows::Revactor::Client
   def initialize(client)
     @client, @rd_args, @ts = client, [ nil ], nil
     io = client.instance_variable_get(:@_io)
-    io.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
+    io.close_on_exec = true
     @kgio_addr = if Revactor::TCP::Socket === client
       @rd_args << RD_ARGS
       client.remote_addr
-- 
EW


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH 5/5] response: simplify regexp
  2015-11-21  8:52 [PATCH 0/5] a few more odds and ends before 5.0 Eric Wong
                   ` (3 preceding siblings ...)
  2015-11-21  8:52 ` [PATCH 4/5] revactor: remove fcntl dependency Eric Wong
@ 2015-11-21  8:52 ` Eric Wong
  4 siblings, 0 replies; 6+ messages in thread
From: Eric Wong @ 2015-11-21  8:52 UTC (permalink / raw)
  To: rainbows-public

Redundant \z statements are ugly and wastes 4 bytes on x86-64
according to ObjectSpace.memsize_of
---
 lib/rainbows/response.rb | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/rainbows/response.rb b/lib/rainbows/response.rb
index ac50321..62dfa39 100644
--- a/lib/rainbows/response.rb
+++ b/lib/rainbows/response.rb
@@ -30,7 +30,7 @@ def write_headers(status, headers, alive, body)
           "Date: #{httpdate}\r\n"
     headers.each do |key, value|
       case key
-      when %r{\A(?:Date\z|Connection\z)}i
+      when %r{\A(?:Date|Connection)\z}i
         next
       when "rack.hijack"
         # this was an illegal key in Rack < 1.5, so it should be
-- 
EW


^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, back to index

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-11-21  8:52 [PATCH 0/5] a few more odds and ends before 5.0 Eric Wong
2015-11-21  8:52 ` [PATCH 1/5] tiny bytecode reductions for cold paths Eric Wong
2015-11-21  8:52 ` [PATCH 2/5] Ruby 1.9.3+-only cleanups Eric Wong
2015-11-21  8:52 ` [PATCH 3/5] response: avoid garbage string entirely Eric Wong
2015-11-21  8:52 ` [PATCH 4/5] revactor: remove fcntl dependency Eric Wong
2015-11-21  8:52 ` [PATCH 5/5] response: simplify regexp Eric Wong

Rainbows! Rack HTTP server user/dev discussion

Archives are clonable:
	git clone --mirror https://bogomips.org/rainbows-public
	git clone --mirror http://ou63pmih66umazou.onion/rainbows-public

Newsgroups are available over NNTP:
	nntp://news.public-inbox.org/inbox.comp.lang.ruby.rainbows
	nntp://ou63pmih66umazou.onion/inbox.comp.lang.ruby.rainbows

 note: .onion URLs require Tor: https://www.torproject.org/
       or Tor2web: https://www.tor2web.org/

AGPL code for this site: git clone https://public-inbox.org/ public-inbox