# \[ANN\] forkoff-1.1.0

**URL:** <https://rubytalk.org/t/ann-forkoff-1-1-0/55765>\
**Category:** ruby-talk\
**Created:** [12 October 2009 03:58 UTC](https://rubytalk.org/t/ann-forkoff-1-1-0/55765 "2009-10-12T03:58:26Z")\
**Posts on this page:** 1\
**Page:** 1

<div class="post-metadata">

**Author:** ![a11](https://yyz1.discourse-cdn.com/flex029/user_avatar/rubytalk.org/a11/32/8169_2.png) [@a11](https://rubytalk.org/u/a11)\
**Post date:** [12 October 2009 03:58 UTC](https://rubytalk.org/t/ann-forkoff-1-1-0/55765/1 "2009-10-12T03:58:26Z")

</div>

NAME

&nbsp;&nbsp;forkoff

SYNOPSIS

&nbsp;&nbsp;brain-dead simple parallel processing for ruby

URI

&nbsp;&nbsp;[http://rubyforge.org/projects/codeforpeople](http://rubyforge.org/projects/codeforpeople)  
&nbsp;&nbsp;[http://github.com/ahoward/forkoff](http://github.com/ahoward/forkoff)

INSTALL

&nbsp;&nbsp;gem install forkoff

DESCRIPTION

&nbsp;&nbsp;forkoff works for any enumerable object, iterating a code block to  
run in a  
&nbsp;&nbsp;child process and collecting the results. forkoff can limit the  
number of  
&nbsp;&nbsp;child processes which is, by default, 2.

SAMPLES

&nbsp;&nbsp;\<========\< samples/a.rb \>========\>

&nbsp;&nbsp;~ \> cat samples/a.rb

&nbsp;&nbsp;&nbsp;&nbsp;# forkoff makes it trivial to do parallel processing with ruby,  
the following  
&nbsp;&nbsp;&nbsp;&nbsp;# prints out each word in a separate process

> **···**
>
> #
> 
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;require 'forkoff'
> 
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;%w( hey you ).forkoff!{|word| puts "#{ word } from #  
> { Process.pid }"}
> 
> &nbsp;&nbsp;~ \> ruby samples/a.rb
> 
> &nbsp;&nbsp;&nbsp;&nbsp;hey from 7907  
> &nbsp;&nbsp;&nbsp;&nbsp;you from 7908
> 
> &nbsp;&nbsp;\<========\< samples/b.rb \>========\>
> 
> &nbsp;&nbsp;~ \> cat samples/b.rb
> 
> &nbsp;&nbsp;&nbsp;&nbsp;# for example, this takes only 4 seconds or so to complete (8  
> iterations  
> &nbsp;&nbsp;&nbsp;&nbsp;# running in two processes = twice as fast)  
> &nbsp;&nbsp;&nbsp;&nbsp;#
> 
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;require 'forkoff'
> 
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;a = Time.now.to\_f
> 
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;results =  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(0..7).forkoff do |i|  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sleep 1  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;i \*\* 2  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end
> 
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;b = Time.now.to\_f
> 
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;elapsed = b - a
> 
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;puts "elapsed: #{ elapsed }"  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;puts "results: #{ results.inspect }"
> 
> &nbsp;&nbsp;~ \> ruby samples/b.rb
> 
> &nbsp;&nbsp;&nbsp;&nbsp;elapsed: 4.19184589385986  
> &nbsp;&nbsp;&nbsp;&nbsp;results: [0, 1, 4, 9, 16, 25, 36, 49]
> 
> &nbsp;&nbsp;\<========\< samples/c.rb \>========\>
> 
> &nbsp;&nbsp;~ \> cat samples/c.rb
> 
> &nbsp;&nbsp;&nbsp;&nbsp;# forkoff does \*NOT\* spawn processes in batches, waiting for each  
> batch to  
> &nbsp;&nbsp;&nbsp;&nbsp;# complete. rather, it keeps a certain number of processes busy  
> until all  
> &nbsp;&nbsp;&nbsp;&nbsp;# results have been gathered. in otherwords the following will  
> ensure that 3  
> &nbsp;&nbsp;&nbsp;&nbsp;# processes are running at all times, until the list is complete.  
> note that  
> &nbsp;&nbsp;&nbsp;&nbsp;# the following will take about 3 seconds to run (3 sets of 3 @ 1  
> second).  
> &nbsp;&nbsp;&nbsp;&nbsp;#
> 
> &nbsp;&nbsp;&nbsp;&nbsp;require 'forkoff'
> 
> &nbsp;&nbsp;&nbsp;&nbsp;pid = Process.pid
> 
> &nbsp;&nbsp;&nbsp;&nbsp;a = Time.now.to\_f
> 
> &nbsp;&nbsp;&nbsp;&nbsp;pstrees =  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;%w( a b c d e f g h i ).forkoff! :processes =\> 3 do |letter|  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sleep 1  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ letter =\> ` pstree -l 2 #{ pid } ` }  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end
> 
> &nbsp;&nbsp;&nbsp;&nbsp;b = Time.now.to\_f
> 
> &nbsp;&nbsp;&nbsp;&nbsp;puts  
> &nbsp;&nbsp;&nbsp;&nbsp;puts "pid: #{ pid }"  
> &nbsp;&nbsp;&nbsp;&nbsp;puts "elapsed: #{ b - a }"  
> &nbsp;&nbsp;&nbsp;&nbsp;puts
> 
> &nbsp;&nbsp;&nbsp;&nbsp;require 'yaml'
> 
> &nbsp;&nbsp;&nbsp;&nbsp;pstrees.each do |pstree|  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;y pstree  
> &nbsp;&nbsp;&nbsp;&nbsp;end
> 
> &nbsp;&nbsp;~ \> ruby samples/c.rb
> 
> &nbsp;&nbsp;&nbsp;&nbsp;pid: 7922  
> &nbsp;&nbsp;&nbsp;&nbsp;elapsed: 3.37899208068848
> 
> &nbsp;&nbsp;&nbsp;&nbsp;---  
> &nbsp;&nbsp;&nbsp;&nbsp;a: |  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-+- 07922 ahoward ruby -Ilib samples/c.rb  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\>-+- 07923 ahoward ruby -Ilib samples/c.rb  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\>-+- 07924 ahoward (ruby)  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\-+- 07925 ahoward ruby -Ilib samples/c.rb
> 
> &nbsp;&nbsp;&nbsp;&nbsp;---  
> &nbsp;&nbsp;&nbsp;&nbsp;b: |  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-+- 07922 ahoward ruby -Ilib samples/c.rb  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\>-+- 07923 ahoward ruby -Ilib samples/c.rb  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\>-+- 07924 ahoward ruby -Ilib samples/c.rb  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\-+- 07925 ahoward ruby -Ilib samples/c.rb
> 
> &nbsp;&nbsp;&nbsp;&nbsp;---  
> &nbsp;&nbsp;&nbsp;&nbsp;c: |  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-+- 07922 ahoward ruby -Ilib samples/c.rb  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\>-+- 07923 ahoward ruby -Ilib samples/c.rb  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\>-+- 07924 ahoward (ruby)  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\-+- 07925 ahoward ruby -Ilib samples/c.rb
> 
> &nbsp;&nbsp;&nbsp;&nbsp;---  
> &nbsp;&nbsp;&nbsp;&nbsp;d: |  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-+- 07922 ahoward ruby -Ilib samples/c.rb  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\>-+- 07932 ahoward ruby -Ilib samples/c.rb  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\>--- 07933 ahoward ruby -Ilib samples/c.rb  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\--- 07934 ahoward ruby -Ilib samples/c.rb
> 
> &nbsp;&nbsp;&nbsp;&nbsp;---  
> &nbsp;&nbsp;&nbsp;&nbsp;e: |  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-+- 07922 ahoward ruby -Ilib samples/c.rb  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\>--- 07932 ahoward (ruby)  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\>-+- 07933 ahoward ruby -Ilib samples/c.rb  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\-+- 07934 ahoward (ruby)
> 
> &nbsp;&nbsp;&nbsp;&nbsp;---  
> &nbsp;&nbsp;&nbsp;&nbsp;f: |  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-+- 07922 ahoward ruby -Ilib samples/c.rb  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\>--- 07932 ahoward (ruby)  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\>-+- 07933 ahoward ruby -Ilib samples/c.rb  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\-+- 07934 ahoward ruby -Ilib samples/c.rb
> 
> &nbsp;&nbsp;&nbsp;&nbsp;---  
> &nbsp;&nbsp;&nbsp;&nbsp;g: |  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-+- 07922 ahoward ruby -Ilib samples/c.rb  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\>-+- 07941 ahoward ruby -Ilib samples/c.rb  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\>--- 07942 ahoward ruby -Ilib samples/c.rb  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\--- 07943 ahoward ruby -Ilib samples/c.rb
> 
> &nbsp;&nbsp;&nbsp;&nbsp;---  
> &nbsp;&nbsp;&nbsp;&nbsp;h: |  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-+- 07922 ahoward ruby -Ilib samples/c.rb  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\>-+- 07941 ahoward (ruby)  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\>-+- 07942 ahoward ruby -Ilib samples/c.rb  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\--- 07943 ahoward ruby -Ilib samples/c.rb
> 
> &nbsp;&nbsp;&nbsp;&nbsp;---  
> &nbsp;&nbsp;&nbsp;&nbsp;i: |  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-+- 07922 ahoward ruby -Ilib samples/c.rb  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\>--- 07942 ahoward (ruby)  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\-+- 07943 ahoward ruby -Ilib samples/c.rb
> 
> &nbsp;&nbsp;\<========\< samples/d.rb \>========\>
> 
> &nbsp;&nbsp;~ \> cat samples/d.rb
> 
> &nbsp;&nbsp;&nbsp;&nbsp;# forkoff supports two strategies of reading the result from the  
> child: via  
> &nbsp;&nbsp;&nbsp;&nbsp;# pipe (the default) or via file. you can select which to use  
> using the  
> &nbsp;&nbsp;&nbsp;&nbsp;# :strategy option.  
> &nbsp;&nbsp;&nbsp;&nbsp;#
> 
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;require 'forkoff'
> 
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;%w( hey you guys ).forkoff :strategy =\> :file do |word|  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;puts "#{ word } from #{ Process.pid }"  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end
> 
> &nbsp;&nbsp;~ \> ruby samples/d.rb
> 
> &nbsp;&nbsp;&nbsp;&nbsp;hey from 7953  
> &nbsp;&nbsp;&nbsp;&nbsp;you from 7954  
> &nbsp;&nbsp;&nbsp;&nbsp;guys from 7955
> 
> HISTORY  
> &nbsp;&nbsp;1.1.0  
> &nbsp;&nbsp;&nbsp;&nbsp;- move to a model with one work queue and signals sent from  
> consumers to  
> &nbsp;&nbsp;&nbsp;&nbsp;producer to noitify ready state. this let's smaller jobs race  
> through a  
> &nbsp;&nbsp;&nbsp;&nbsp;single process even while a larger job may have one sub-process  
> bound up.  
> &nbsp;&nbsp;&nbsp;&nbsp;incorporates a fix from [http://github.com/fredrikj/forkoff](http://github.com/fredrikj/forkoff) which  
> meant  
> &nbsp;&nbsp;&nbsp;&nbsp;some processes would lag behind when jobs didn't have similar  
> execution  
> &nbsp;&nbsp;&nbsp;&nbsp;times.
> 
> &nbsp;&nbsp;1.0.0  
> &nbsp;&nbsp;&nbsp;&nbsp;- move to github
> 
> &nbsp;&nbsp;0.0.4  
> &nbsp;&nbsp;&nbsp;&nbsp;- code re-org  
> &nbsp;&nbsp;&nbsp;&nbsp;- add :strategy option  
> &nbsp;&nbsp;&nbsp;&nbsp;- default number of processes is 2, not 8
> 
> &nbsp;&nbsp;0.0.1
> 
> &nbsp;&nbsp;&nbsp;&nbsp;- updated to use producer threds pushing onto a SizedQueue for  
> each consumer  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;channel. in this way the producers do not build up a massize  
> parllel data  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;structure but provide data to the consumers only as fast as they  
> can fork  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;and proccess it. basically for a 4 process run you'll end up  
> with 4  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;channels of size 1 between 4 produces and 4 consumers, each  
> consumer is a  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;thread popping of jobs, forking, and yielding results.
> 
> &nbsp;&nbsp;&nbsp;&nbsp;- removed use of Queue for capturing the output. now it's simply  
> an array  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;of arrays which removed some sync overhead.
> 
> &nbsp;&nbsp;&nbsp;&nbsp;- you can configure the number of processes globally with
> 
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Forkoff.default['proccess'] = 4
> 
> &nbsp;&nbsp;&nbsp;&nbsp;- you can now pass either an options hash
> 
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;forkoff( :processes =\> 2 ) ...
> 
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;or plain vanilla number
> 
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;forkoff( 2 ) ...
> 
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;to the forkoff call
> 
> &nbsp;&nbsp;&nbsp;&nbsp;- default number of processes is 8, not 2
> 
> &nbsp;&nbsp;0.0.0
> 
> &nbsp;&nbsp;&nbsp;&nbsp;initial version
> 
> enjoy.
