Fibur is a library that allows concurrency during Ruby I/O operations without needing to make use of callback systems. Traditionally in Ruby, to achieve concurrency during blocking I/O operations, programmers would make use of Fibers and callbacks. Fibur eliminates the need for wrapping your I/O calls with Fibers and a callback. It allows you to write your blocking I/O calls the way you normally would, and still have concurrent execution during those I/O calls.
Say you have a method that fetches data from a network resource:
require 'net/http'
def network_read uri
Net::HTTP.get_response uri
end
We need to fetch that data say 100 times, so we'll wrap it in a loop:
100.times { network_read }
If we benchmark this code:
require 'benchmark'
require 'net/http'
require 'uri'
def network_read uri
Net::HTTP.get_response uri
end
uri = URI('http://google.com/')
Benchmark.bm do |x|
x.report('loop') { 100.times { network_read uri } }
end
On my machine it takes about 5 seconds:
$ ruby test.rb
user system total real
loop 0.210000 0.070000 0.280000 ( 5.731776)
Now lets modify our benchmark to wrap each call to network_read
in a Fibur:
require 'benchmark'
require 'net/http'
require 'uri'
require 'fibur' # use the Fibur gem.
def network_read uri
Net::HTTP.get_response uri
end
uri = URI('http://google.com/')
Benchmark.bm(5) do |x|
x.report('loop') { 100.times { network_read uri } }
x.report('fibur') {
100.times.map {
Fibur.new { network_read uri }
}.map(&:join)
}
end
Output from our benchmark:
$ ruby -I. test.rb
user system total real
loop 0.220000 0.070000 0.290000 ( 5.732683)
fibur 0.110000 0.050000 0.160000 ( 0.197434)
Wrapping each call to network_read
in a fibur brought the time down to 0.2 seconds! Using Fiburs, we were able to gain full concurrency during our I/O operations, and we didn't have to modify our network_read
method.
Fibur only works on Ruby 1.9, and you can get it by installing the fibur
gem.
I encourage you to check out the source.
@wycats Semantics? Ok; sure.
Let X represent the maximum amount of RAM present on the system. Let Y represent the memory consumed by a single average thread (or "fibur"). The calculation of Y is determined by running a given simulation to steady-state under expected load and dividing actual memory used,
Z
by number of real threads,Y'
. If a single thread is allocated per incoming connection then the maximum concurrency of the system is defined by X/Y. A proof by contradiction is left to the reader.