Skip to content

Instantly share code, notes, and snippets.

@tenderlove
Created December 19, 2011 18:09
Show Gist options
  • Star 45 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save tenderlove/1498215 to your computer and use it in GitHub Desktop.
Save tenderlove/1498215 to your computer and use it in GitHub Desktop.

Fibur

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.

Example

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.

Installation

Fibur only works on Ruby 1.9, and you can get it by installing the fibur gem.

How does it work?

I encourage you to check out the source.

@wycats
Copy link

wycats commented Dec 19, 2011

@indexzero nope.

ruby-1.9.3-p0 :001 > Fibur = Thread
=> Thread
ruby-1.9.3-p0 :002 > def memory() ps -orss #{Process.pid}.split("\n")[1].strip end
=> nil
ruby-1.9.3-p0 :003 > original = memory.to_i
=> 10928
ruby-1.9.3-p0 :004 > 2000.times { Fibur.new { sleep } }
=> 2000
ruby-1.9.3-p0 :005 > final = memory.to_i
=> 52496
ruby-1.9.3-p0 :006 > (final - original) / 2000
=> 20

A Fibur does not use up 8MB of memory simply by existing. It starts off small and then grows as stack size is consumed.

@indexzero
Copy link

@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.

@cararemixed
Copy link

@indexzero, likewise, any allocated state for event emitters, event sources, tick queues, and all the other overhead node uses for tracking state in the system. One might be a bit cheaper (it's actually much more complicated than it looks to decide this case) but it's optimizing a constant (both are linear cost) and arguing that efficient threads aren't scalable is just FUD.

It'd be a lot more effective if proponents of thread alternatives wouldn't point out that threads don't work. Rather, why not show how other concepts have strengths. Event loops are, for example, awesome and people should use them where appropriate.

@wycats
Copy link

wycats commented Dec 20, 2011

@indexzero @strmpnk that was exactly my point. Your argument holds true for threads and fibers equally, except that fibers might be a (small) constant multiple cheaper. People assume that threads consume 8MB of RAM, while fibers consume only 4k, and therefore threads are 2000 times less efficient. My comment disproved that.

@argent-smith
Copy link

U ROOL!!!

@kschiess
Copy link

Event loops are the goto of the new century.

@cris
Copy link

cris commented Dec 20, 2011

@tenderlove: will you ship it with upcoming Rails 3.2?

@gerhard
Copy link

gerhard commented Dec 20, 2011

This is F U C K I N G genius! Best gem of 2011.

@chrisfinne
Copy link

Best library I've seen since https://github.com/lazyatom/acts_as_hasselhoff

@jwoertink
Copy link

HAHAH. awesome. I don't know what some of you talking about there's no tests. Looks like a full test coverage to me :p

@joelparkerhenderson
Copy link

Really excited to hear about the feature roadmap for Fibur 2.0.

@ZhangHanDong
Copy link

太坑爹了!

HaHaHa. The test is awesome.

@zekus
Copy link

zekus commented Feb 2, 2012

LOOOOL this is the best library ever made!

@hemanth
Copy link

hemanth commented Jun 27, 2012

Fibur = Thread 👍

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