Psyllium: Makes using Ruby Fibers easier
Psyllium | SIL-ee-um |
- Dietary the seed of a fleawort (especially Plantago psyllium). Mainly used as a supplement to improve dietary fiber consumption.
- Programming a Ruby gem to improve the experience of using Ruby Fiber primitives.
What is Psyllium?
Psyllium is a library to make it easier to use Ruby Fibers for everyday programming.
Ruby version 3 introduced the Fiber Scheduler interface, which makes it easier to use Fibers for concurrent programming. However, native Thread objects still have several useful methods that Fibers do not have.
The Psyllium library adds many of these methods to the builtin Fiber class such
as start
, join
, value
, and others to make it easier to replace Thread
usage with Fiber usage, or to mix and match Thread and Fiber usage without
concern for which concurrency primitive is being used.
Assuming that a Fiber Scheduler is set, Psyllium Fibers can be used in ways similar to Threads, with a similar interface, and with the added benefit of much lower memory usage compared to native Threads.
Why Psyllium?
Psyllium makes it easier to use auto-scheduled fibers and to block on their execution.
Before Psyllium, the Fiber interface seemed to be centered around two types of usage: it was assumed that Fibers would be used in one of two ways:
- (Before Ruby 3) Explicitly and manually manipulated using
Fiber.resume
,Fiber.yield
, andFiber.alive?
. - (After Ruby 3) Fired off and forgotten about. In essence, left to the scheduler to deal with. If you want a final value back you must use some separate mechanism to track and retrieve it.
With Psyllium, it is possible to call join
on a Fiber, just like a Thread.
Assuming other Fibers are simultaneously scheduled, they will continue
executing concurrently until the Fiber in question finishes joining.
It is also possible to call value
to retrieve the final value (or exception)
returned from the block given to Fiber.start
, in the exact same way as a
Thread. And just like with a Thread, calling value
will first implicitly
join
the Fiber. It is also possible to give a timeout limit when calling
join
on a Fiber, just like with a Thread.
By using Fibers in this way, instead of Threads, memory usage can be significantly reduced. Potentially thousands of Fibers can be spawned and joined at a fraction of the memory cost of native Threads.
If your Ruby application directly manipulates Threads or Thread pools, and those Threads spend most (or all) of their time just waiting on IO, then consider trying Psyllium enhanced Fibers instead of Threads.
Why not Psyllium?
Circumstances where you shouldn't use Psyllium (or Fibers generally):
- Your application is compute heavy. It uses Threads that call out to FFI code and release the GVL when doing so (i.e. truly parallel code execution).
- If you don't have concurrent code at all (i.e. the code must run serially).
Installation
Install the gem and add to the application's Gemfile by executing:
bundle add psyllium
If bundler is not being used to manage dependencies, install the gem by executing:
gem install psyllium
Usage
Instead of doing the following in your code:
thread1 = Thread.start { long_running_io_operation_with_result1() }
thread2 = Thread.start { long_running_io_operation_with_result2() }
thread1.join
thread2.join
# `value` implicitly calls `join`, so the explicit `join` calls above are
# not strictly necessary.
value1 = thread1.value
value2 = thread2.value
You can now do this:
# Adds new methods to Fiber
require 'psyllium'
# Calls to `Fiber.start` will fail if no scheduler is set beforehand.
Fiber.set_scheduler(SomeSchedulerImplementation.new)
fiber1 = Fiber.start { long_running_io_operation_with_result1() }
fiber2 = Fiber.start { long_running_io_operation_with_result2() }
fiber1.join
fiber2.join
# `value` implicitly calls `join`, so the explicit `join` calls above are
# not strictly necessary.
value1 = fiber1.value
value2 = fiber2.value
Development
After checking out the repo, run bin/setup
to install dependencies.
Then, run rake test
to run the tests.
You can also run bin/console
for an interactive prompt
that will allow you to experiment.
To install this gem onto your local machine, run bundle exec rake install
.
To release a new version, update the version number in version.rb
,
and then run bundle exec rake release
, which will create a git tag for the version,
push git commits and the created tag, and push the .gem
file to rubygems.org.
Contributing
Bug reports and pull requests are welcome on GitHub at: https://github.com/eestrada/psyllium