What Everyone Should Know About Fibers in Ruby

If you want to get the most performance out of your Ruby projects we have to explore fibers & how they work.

Let’s start with a simple question…

What are fibers?

Fibers are workers, they run code & keep track of their own progress. In other words, fibers are a concurrency mechanism.

Yes!

Just like threads. With the difference that you have more control over fibers than threads.

In what sense you have more control?

Well.

Your operating system decides when to run threads & when to pause them.

That’s not the case with fibers!

You have to tell a fiber exactly when to run & when to stop.

Fibers vs Threads

Threads feel like they’re running in the background, doing their thing.

Fibers don’t to this.

While a fiber is running it becomes the main program until you stop it.

Let’s look at a code example!

Using Fibers: An Example

Create a fiber with Fiber.new & a block.

Example:

f = Fiber.new { puts 1 }

Then run the fiber using the resume method.

Like this:

f.resume

This prints 1 & it returns control to your main program.

But how do you stop a fiber?

With the Fiber.yield method, which is different from the yield keyword used for blocks.

Example:

f = Fiber.new { puts 1; Fiber.yield; puts 2 }

f.resume
# 1

f.resume
# 2

This will print 1 after starting the fiber with resume, then stop.

If you call resume again…

It will continue exactly from where it left & print 2.

Note: Resuming this fiber another time results in FiberError: dead fiber called because there is no more code to run.

This is why fibers are useful!

Calling Fiber.yield inside a fiber is like pressing a pause button. Allowing you to stop in the middle of loops, or any code you write inside a fiber’s block.

Fibers & Loops: Endless Sequences

We can use this “pause button” effect to create an infinite sequence.

Ingredients:

  • Fibers
  • Loop
  • Counter

For example, factorial numbers:

factorial =
Fiber.new do
  count = 1

  loop do
    Fiber.yield (1..count).inject(:*)
    count += 1
  end
end

You can use this fiber as many times as you want, with the resume method, to get the next number in the sequence.

Example:

Array.new(5) { factorial.resume }

# [1, 2, 6, 24, 120]

Nice!

Fibers & IO: Async Operations

Fibers are faster & more efficient than threads for things you have to wait for, like network connections.

Why?

Because fibers result in less context switches.

A context switch is when the CPU changes from the current task to another task.

This has a small cost, but it adds up!

I did some testing using Linux’s perf tool, and I found that in a simple Ruby application, fibers generate 3 times less context switches than threads.

Sounds good?

You can start using fibers right now:

Remember…

There are no magic bullets, but it’s worth testing & see what fibers can do for you 🙂

Summary

You have learned about Fibers in Ruby! A fiber allows you to create a unit of code that can be paused & resumed at your own will.

Now it’s your turn to give this a try.

Thanks for reading!

Leave a Comment:

2 comments
Maciej says 3 weeks ago

Hi Jesus,
Thank you for this article. I’ve got some question. If fibers are so cool why falcon webserver is not so popular as puma?? When should I use this webserver instead of puma/unicorn/passenger??

Kind Regards

Reply
    Jesus Castello says 3 weeks ago

    Hi,
    Falcon is not 1.0 yet, so that’s a thing.

    You can try it & run some benchmarking software like siege / ab / wrk to see how it does with your app, also look at your memory usage over time.

    It’s something that can help you get some extra performance, but if you don’t have thousands of active users in your app it’s probably not going to matter which server you choose.

    Reply
Add Your Reply