mperham / connection_pool

Generic connection pooling for Ruby
MIT License
1.63k stars 143 forks source link

optimize nested connection implementation #96

Closed ThomasSevestre closed 7 years ago

ThomasSevestre commented 7 years ago

This PR optimize nested connection implementation It gives a 10 to 20% optimisation of ConnectionPool.with

# Ruby 2.3
# user     system      total        real
# normal  0.660000   0.010000   0.670000 (  0.696400)
# optim   0.550000   0.010000   0.560000 (  0.571204)
# 
# Ruby 2.4
# user     system      total        real
# normal  0.620000   0.010000   0.630000 (  0.630224)
# optim   0.550000   0.000000   0.550000 (  0.556212)

require 'benchmark'
require 'connection_pool'

class FastConnectionPool < ConnectionPool
  def initialize(*args)
    super
    @key_count= :"#{@key}-count"
  end

  def checkout(options = {})
    if ::Thread.current[@key]
      ::Thread.current[@key_count]+= 1
      ::Thread.current[@key]
    else
      ::Thread.current[@key_count]= 1
      ::Thread.current[@key]= @available.pop(options[:timeout] || @timeout)
    end
  end

  def checkin
    if ::Thread.current[@key]
      if ::Thread.current[@key_count] == 1
        @available.push(::Thread.current[@key])
        ::Thread.current[@key]= nil
      else
        ::Thread.current[@key_count]-= 1
      end
    else
      raise ConnectionPool::Error, 'no connections are checked out'
    end

    nil
  end
end

pool= ConnectionPool.new { 1 }
fpool= FastConnectionPool.new { 1 }

n = 100000
Benchmark.bm do |x|
  x.report("normal") { n.times { pool.with{} } }
  x.report("optim ") { n.times { fpool.with{} } }
end
ThomasSevestre commented 7 years ago

I've tested locally with ruby 2.4.1 and I don't reproduce the problem detected by travis

mperham commented 7 years ago

Looks like the += and -= operators don't need a mutex because the counter is not actually shared data.