Skip to content

Instantly share code, notes, and snippets.

@sferik
Last active May 5, 2017 05:51
Show Gist options
  • Save sferik/39831f34eb87686b639c to your computer and use it in GitHub Desktop.
Save sferik/39831f34eb87686b639c to your computer and use it in GitHub Desktop.
module Enumerable
def first_to_finish
threads = collect { |args| Thread.new { yield(args) } }
loop until done = threads.detect { |t| !t.alive? }
threads.each(&:kill)
done.value
end
end
puts [5, 3, 1, 2, 4].first_to_finish { |x| sleep x }
require "benchmark/ips"
require "bcrypt"
module Enumerable
def first_to_finish
threads = collect { |args| Thread.new { yield(args) } }
loop until done = threads.detect { |t| !t.alive? }
threads.each(&:kill)
done.value
end
def first_to_finish_with_queue
queue = Queue.new
threads = collect { |args| Thread.new { queue << yield(args) } }
result = queue.pop
threads.each(&:kill)
result
end
def get_first_result_async
result = nil
threads = map do |args|
Thread.new do
if current_result = yield(args)
result = current_result
(threads - [Thread.current]).each(&:kill) # kill siblings
end
end
end
threads.each(&:join)
result
end
end
COSTS = (10..15).to_a.reverse
def sferik
COSTS.first_to_finish { |cost| BCrypt::Password.create("secret", :cost => cost) }
end
def choonkeat
COSTS.first_to_finish_with_queue { |cost| BCrypt::Password.create("secret", :cost => cost) }
end
def juanito
COSTS.get_first_result_async { |cost| BCrypt::Password.create("secret", :cost => cost) }
end
Benchmark.ips do |x|
x.report("@sferik") { sferik }
x.report("@choonkeat") { choonkeat }
x.report("@JuanitoFatas") { juanito }
x.compare!
end
@choonkeat
Copy link

awesome that was fun @sferik 👏 👏 👏

btw since we're killing threads, I suspect those lambda arguments need to be wrapped with Thread.handle_interrupt (or are they unrelated?)

folding the lessons here into choonkeat/attache#27 thanks guys

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