-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
WorkerPool and remote() #15073
WorkerPool and remote() #15073
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1223,6 +1223,7 @@ export | |
pmap, | ||
procs, | ||
put!, | ||
remote, | ||
remotecall, | ||
remotecall_fetch, | ||
remotecall_wait, | ||
|
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,75 @@ | ||||||
# This file is a part of Julia. License is MIT: http://julialang.org/license | ||||||
|
||||||
|
||||||
type WorkerPool | ||||||
channel::RemoteChannel{Channel{Int}} | ||||||
end | ||||||
|
||||||
|
||||||
""" | ||||||
WorkerPool([workers]) | ||||||
|
||||||
Create a WorkerPool from a vector of worker ids. | ||||||
""" | ||||||
function WorkerPool(workers=Int[]) | ||||||
|
||||||
# Create a shared queue of available workers... | ||||||
pool = WorkerPool(RemoteChannel(()->Channel{Int}(128))) | ||||||
|
||||||
# Add workers to the pool... | ||||||
for w in workers | ||||||
put!(pool, w) | ||||||
end | ||||||
|
||||||
return pool | ||||||
end | ||||||
|
||||||
|
||||||
put!(pool::WorkerPool, w::Int) = put!(pool.channel, w) | ||||||
put!(pool::WorkerPool, w::Worker) = put!(pool.channel, w.id) | ||||||
|
||||||
isready(pool::WorkerPool) = isready(pool.channel) | ||||||
|
||||||
take!(pool::WorkerPool) = take!(pool.channel) | ||||||
|
||||||
|
||||||
""" | ||||||
remotecall_fetch(f, pool::WorkerPool, args...) | ||||||
|
||||||
Call `f(args...)` on one of the workers in `pool`. | ||||||
""" | ||||||
function remotecall_fetch(f, pool::WorkerPool, args...) | ||||||
worker = take!(pool) | ||||||
try | ||||||
remotecall_fetch(f, worker, args...) | ||||||
finally | ||||||
put!(pool, worker) | ||||||
end | ||||||
end | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nice one |
||||||
|
||||||
|
||||||
""" | ||||||
default_worker_pool | ||||||
|
||||||
WorkerPool containing idle `workers()` (used by `remote(f)`). | ||||||
""" | ||||||
_default_worker_pool = Nullable{WorkerPool}() | ||||||
function default_worker_pool() | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
It should be the same list as There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As a first step, I've changed A future PR could automatically call There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How about
|
||||||
if isnull(_default_worker_pool) && myid() == 1 | ||||||
set_default_worker_pool(WorkerPool()) | ||||||
end | ||||||
return get(_default_worker_pool) | ||||||
end | ||||||
|
||||||
function set_default_worker_pool(p::WorkerPool) | ||||||
global _default_worker_pool = Nullable(p) | ||||||
end | ||||||
|
||||||
|
||||||
""" | ||||||
remote(f) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. needs to go into the rst docs if it's going to be exported There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How about calling it There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unless there is a solid convention that functions that return a function have an I'd be happy for it to be changed in a later functions-that-return-functions naming cleanup PR. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. .rst update done |
||||||
|
||||||
Returns a lambda that executes function `f` on an available worker | ||||||
using `remotecall_fetch`. | ||||||
""" | ||||||
remote(f) = (args...)->remotecall_fetch(f, default_worker_pool(), args...) |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,6 +11,67 @@ elseif Base.JLOptions().code_coverage == 2 | |
end | ||
addprocs(3; exeflags=`$cov_flag $inline_flag --check-bounds=yes --depwarn=error`) | ||
|
||
# Test remote() | ||
|
||
let | ||
pool = Base.default_worker_pool() | ||
|
||
count = 0 | ||
count_condition = Condition() | ||
|
||
function remote_wait(c) | ||
@async begin | ||
count += 1 | ||
remote(take!)(c) | ||
count -= 1 | ||
notify(count_condition) | ||
end | ||
yield() | ||
end | ||
|
||
testchannels = [RemoteChannel() for i in 1:nworkers()] | ||
testcount = 0 | ||
@test isready(pool) == true | ||
for c in testchannels | ||
@test count == testcount | ||
remote_wait(c) | ||
testcount += 1 | ||
end | ||
@test count == testcount | ||
@test isready(pool) == false | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. May be worthwhile to make these and below tests work with any number of workers by re-writing like
This way if the number of workers changes in the future we do not have to revisit this block of tests. Also more readable. |
||
|
||
for c in testchannels | ||
@test count == testcount | ||
put!(c, "foo") | ||
testcount -= 1 | ||
wait(count_condition) | ||
@test count == testcount | ||
@test isready(pool) == true | ||
end | ||
|
||
@test count == 0 | ||
|
||
for c in testchannels | ||
@test count == testcount | ||
remote_wait(c) | ||
testcount += 1 | ||
end | ||
@test count == testcount | ||
@test isready(pool) == false | ||
|
||
for c in reverse(testchannels) | ||
@test count == testcount | ||
put!(c, "foo") | ||
testcount -= 1 | ||
wait(count_condition) | ||
@test count == testcount | ||
@test isready(pool) == true | ||
end | ||
|
||
@test count == 0 | ||
end | ||
|
||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We have had tests hitting upper limits and timing out on CI. Would be better to rewrite these tests with a different strategy so that they do not add 10 seconds to the parallel test run. |
||
id_me = myid() | ||
id_other = filter(x -> x != id_me, procs())[rand(1:(nprocs()-1))] | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Need to add a default constructor here to avoid the following build warning :
WARNING: Method definition (::Type{Base.WorkerPool})(Any) in module Base at workerpool.jl:5 overwritten at workerpool.jl:17
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See https://github.com/JuliaLang/julia/pull/15409/commits