Concurrency, parallelism,
and related mind fuckery
concepts.
@_______kim
T1
Time
T2
# sequential.rb
require 'benchmark'
Benchmark.bm do |x|
x.report 'sequential:' do
10_000_000.times { 2 + 2 }
end
end
> ruby sequential.rb
user system total real
0.620000 0.000000 0.620000 ( 0.618819)
Processes
fork do
# things
end
# processes.rb
require 'benchmark'
Benchmark.bm do |x|
x.report 'sequential:' do
10_000_000.times { 2 + 2 }
end
x.report ' processes:' do
Process.fork { 5_000_000.times { 2 + 2 } }
Process.fork { 5_000_000.times { 2 + 2 } }
Process.wait
end
end
> ruby processes.rb
user system total real
sequential: 0.650000 0.000000 0.650000 ( 0.640821)
processes: 0.000000 0.000000 0.310000 ( 0.319911)
Threads
# threads.rb
require 'benchmark'
Benchmark.bm do |x|
x.report 'sequential:' do
10_000_000.times { 2 + 2 }
end
x.report ' threads:' do
a = Thread.new { 5_000_000.times { 2 + 2 } }
b = Thread.new { 5_000_000.times { 2 + 2 } }
[a, b].each(&:join)
end
end
> ruby threads.rb
user system total real
sequential: 0.620000 0.000000 0.620000 ( 0.622752)
threads: 0.620000 0.000000 0.620000 ( 0.622439)
Global Interpreter Lock
(GIL)
# threads_redux.rb
require 'benchmark'
Benchmark.bm do |x|
x.report('sequential:') do
sleep 1
sleep 1
end
x.report(' threads:') do
a = Thread.new { sleep 1 }
b = Thread.new { sleep 1 }
[a, b].map(&:join)
end
end
> ruby threads_redux.rb
user system total real
sequential: 0.000000 0.000000 0.000000 ( 2.000341)
threads: 0.000000 0.000000 0.000000 ( 1.000932)
Fibers
fiber = Fiber.new do |a|
# Do some things
b = Fiber.yield a + 2
# Something else
c = Fiber.yield b + 3
# And again...
c + 4
end
puts fiber.resume 10 # a
#=> 12
puts fiber.resume 14 # b
#=> 17
puts fiber.resume 16 # c
#=> 20
puts fiber.resume 18
#=> FiberError: dead fiber called
-
Full-featured event loop backed by epoll, kqueue, IOCP, event ports.
-
Asynchronous TCP and UDP sockets
-
Asynchronous DNS resolution
-
Asynchronous file and file system operations
-
File system events
-
ANSI escape code controlled TTY
-
IPC with socket sharing, using Unix domain sockets or named pipes (Windows)
-
Child processes
-
Thread pool
-
Signal handling
-
High resolution clock
-
Threading and synchronization primitives
require 'libuv'
reactor do |reactor|
# Default reactor
reactor.timer {
puts "5 seconds passed"
}.start(5000)
puts "this will print first"
end
puts "reactor stopped. No more IO to process"
Promises
# :: Proc -> Promise
reactor.work
reactor do |reactor|
# Perform some work on the thread pool
reactor.work {
2 + 2
}.then { |result|
puts "result using a promise #{result}"
}.catch { |error|
puts "oh noes!"
}
end
t1 = reactor.work { ... }
t2 = reactor.work { ... }
reactor.any(t1, t2).then do |x|
# ...
end
reactor.all(t1, t2).then do |a, b|
c = a + b
end
Futures
a = t1.value
b = t2.value
c = a + b
t1 = reactor.work { ... }
t2 = reactor.work { ... }
x = reactor.work { ... }
begin
puts "the thing was #{x.value}"
rescue => error
puts "oh noes!"
end
acaprojects.com/jobs
Concurrent Ruby
By Kim Burgess
Concurrent Ruby
- 134