Skip to content

Instantly share code, notes, and snippets.

@elct9620
Last active August 16, 2018 08:01
Show Gist options
  • Save elct9620/9651456ff3abf820065b1e1f98da37ff to your computer and use it in GitHub Desktop.
Save elct9620/9651456ff3abf820065b1e1f98da37ff to your computer and use it in GitHub Desktop.
require 'net/http'
require 'uri'
require 'socket'
require 'openssl'
require 'fiber'
require 'benchmark'
require 'benchmark/ips'
require 'benchmark/memory'
# :nodoc:
class Selector
def initialize
@readable = {}
end
def wait_readable(io)
Fiber.new do
@readable[io] = Fiber.current
Fiber.yield
yield
end.resume
end
def resume
readable, = IO.select(@readable.keys)
readable.each do |io|
fiber = @readable.delete(io)
fiber.resume
end
end
def run
resume until @readable.empty?
end
end
# :nodoc:
class AsyncHTTP
attr_reader :uri, :response, :selector
def initialize(url, selector = nil)
@uri = URI(url)
@selector = selector
@response = ''
end
def client
@socket ||= TCPSocket.new uri.hostname, uri.port
@client ||= OpenSSL::SSL::SSLSocket.new(@socket)
end
def get
client.connect
client.sync_close = true
send_request
read_response
_, @response = @response.split("\n\r")
yield response if block_given?
response
end
private
def send_request
client.puts "GET #{uri.path} HTTP/1.0"
client.puts "Host: #{uri.hostname}"
client.puts
end
def read_response
loop do
break if client.eof?
@response << client.read_nonblock(1)
end
rescue OpenSSL::SSL::SSLErrorWaitReadable
return read_coroutine if selector
retry
end
def read_coroutine
selector.wait_readable(client) do
read_response
end
end
end
# :nodo:
class Logger
attr_reader :stdout
def initialize(stdout = true)
@stdout = stdout
end
def puts(*args)
return unless stdout
STDOUT.puts(*args)
end
end
def async(url, type, currency, selector, logger)
AsyncHTTP.new(format(url, type: type, currency: currency), selector).get { |x| logger.puts x }
end
def plain(url, type, currency, logger)
logger.puts Net::HTTP.get(URI(format(url, type: type, currency: currency)))
end
def thread(url, type, currency, logger)
Thread.new { plain(url, type, currency, logger) }
end
MARKET = [
'https://api.bitfinex.com/v1/pubticker/%<type>s%<currency>s',
'https://bittrex.com/api/v1.1/public/getmarketsummary?market=%<currency>s-%<type>s',
'https://api.binance.com/api/v1/ticker/24hr?symbol=%<type>s%<currency>s',
'https://api.hitbtc.com/api/2/public/ticker/%<type>s%<currency>s'
]
PAIRS = [
['BTC', 'USD'],
['ETH', 'USD']
]
def make
MARKET.map do |url|
PAIRS.map do |type, currency|
yield url, type, currency
end
end.flatten
end
logger = Logger.new(false)
selector = Selector.new
# AsyncHTTP.new(format(ENDPOINT, type: 'BTC', currency: 'USD'), selector).get { |x| puts x }
# AsyncHTTP.new(format(ENDPOINT, type: 'ETH', currency: 'USD'), selector).get { |x| puts x }
# selector.run
Benchmark.ips do |x|
x.report('Fiber') do |count|
count.times do
make do |url, type, currency|
async(url, type, currency, selector, logger)
end
selector.run
sleep 5
end
end
x.report('Net::HTTP') do |count|
count.times do
make do |url, type, currency|
plain(url, type, currency, logger)
end
sleep 5
end
end
x.report('[P] Net::HTTP') do |count|
count.times do
make do |url, type, currency|
thread(url, type, currency, logger)
end.map(&:join)
sleep 5
end
end
x.compare!
end
Benchmark.memory do |x|
x.report('Fiber') do |count|
make do |url, type, currency|
async(url, type, currency, selector, logger)
end
selector.run
end
x.report('Net::HTTP') do |count|
make do |url, type, currency|
plain(url, type, currency, logger)
end
end
x.report('[P] Net::HTTP') do |count|
make do |url, type, currency|
thread(url, type, currency, logger)
end.map(&:join)
end
x.compare!
end
@elct9620
Copy link
Author

Warming up --------------------------------------
               Fiber     1.000  i/100ms
           Net::HTTP     1.000  i/100ms
       [P] Net::HTTP     1.000  i/100ms
Calculating -------------------------------------
               Fiber      0.150  (± 0.0%) i/s -      1.000  in   6.650212s
           Net::HTTP      0.171  (± 0.0%) i/s -      1.000  in   5.855137s
       [P] Net::HTTP      0.161  (± 0.0%) i/s -      1.000  in   6.215406s

Comparison:
           Net::HTTP:        0.2 i/s
       [P] Net::HTTP:        0.2 i/s - 1.06x  slower
               Fiber:        0.2 i/s - 1.14x  slower

Calculating -------------------------------------
               Fiber     1.141M memsize (    54.378k retained)
                         5.663k objects (   356.000  retained)
                        50.000  strings (    50.000  retained)
           Net::HTTP     2.291M memsize (     5.636k retained)
                         1.656k objects (    51.000  retained)
                        50.000  strings (    50.000  retained)
       [P] Net::HTTP     4.393M memsize (     5.636k retained)
                         1.670k objects (    51.000  retained)
                        50.000  strings (    50.000  retained)

Comparison:
               Fiber:    1140608 allocated
           Net::HTTP:    2291452 allocated - 2.01x more
       [P] Net::HTTP:    4392557 allocated - 3.85x more

@elct9620
Copy link
Author

Warming up --------------------------------------
               Fiber     1.000  i/100ms
           Net::HTTP     1.000  i/100ms
       [P] Net::HTTP     1.000  i/100ms
Calculating -------------------------------------
               Fiber      0.173  (± 0.0%) i/s -      1.000  in   5.787644s
           Net::HTTP      0.173  (± 0.0%) i/s -      1.000  in   5.766318s
       [P] Net::HTTP      0.162  (± 0.0%) i/s -      1.000  in   6.155530s

Comparison:
           Net::HTTP:        0.2 i/s
               Fiber:        0.2 i/s - 1.00x  slower
       [P] Net::HTTP:        0.2 i/s - 1.07x  slower

Calculating -------------------------------------
               Fiber     1.147M memsize (    55.746k retained)
                         5.681k objects (   356.000  retained)
                        50.000  strings (    50.000  retained)
           Net::HTTP     2.276M memsize (     5.416k retained)
                         1.656k objects (    50.000  retained)
                        50.000  strings (    50.000  retained)
       [P] Net::HTTP     5.457M memsize (     5.343k retained)
                         1.876k objects (    51.000  retained)
                        50.000  strings (    50.000  retained)

Comparison:
               Fiber:    1146951 allocated
           Net::HTTP:    2276040 allocated - 1.98x more
       [P] Net::HTTP:    5457467 allocated - 4.76x more

@elct9620
Copy link
Author

Warming up --------------------------------------
               Fiber     1.000  i/100ms
           Net::HTTP     1.000  i/100ms
       [P] Net::HTTP     1.000  i/100ms
Calculating -------------------------------------
               Fiber      0.163  (± 0.0%) i/s -      1.000  in   6.128085s
           Net::HTTP      0.175  (± 0.0%) i/s -      1.000  in   5.714892s
       [P] Net::HTTP      0.187  (± 0.0%) i/s -      1.000  in   5.344719s

Comparison:
       [P] Net::HTTP:        0.2 i/s
           Net::HTTP:        0.2 i/s - 1.07x  slower
               Fiber:        0.2 i/s - 1.15x  slower

Calculating -------------------------------------
               Fiber     1.138M memsize (    54.378k retained)
                         5.657k objects (   356.000  retained)
                        50.000  strings (    50.000  retained)
           Net::HTTP     2.292M memsize (     5.635k retained)
                         1.656k objects (    51.000  retained)
                        50.000  strings (    50.000  retained)
       [P] Net::HTTP     4.410M memsize (     5.523k retained)
                         1.694k objects (    52.000  retained)
                        50.000  strings (    50.000  retained)

Comparison:
               Fiber:    1138499 allocated
           Net::HTTP:    2292285 allocated - 2.01x more
       [P] Net::HTTP:    4409746 allocated - 3.87x more

@elct9620
Copy link
Author

When we try to send more request, the Fiber way will use more memory than Thread way.
But we can increment the buffer read from OpenSSL, and it will reduce more memory than Thready. way.

https://gist.github.com/elct9620/9651456ff3abf820065b1e1f98da37ff#file-async-http-rb-L73

read_nonblock(1)

Change it to

read_nonblock(102400)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment