forked from JuliaParallel/MPI.jl
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request JuliaParallel#160 from JuliaParallel/op_create
support arbitrary Julia functions in reduction operations
- Loading branch information
Showing
6 changed files
with
126 additions
and
50 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
# Implement user-defined MPI reduction operations, by passing Julia | ||
# functions as callbacks to MPI. | ||
|
||
# for defining thread-local variables; can use Compat | ||
# once JuliaLang/Compat.jl#223 is resolved. | ||
if isdefined(Base, :Threads) | ||
import Base.Threads: nthreads, threadid | ||
else | ||
nthreads() = 1 | ||
threadid() = 1 | ||
end | ||
|
||
# Unfortunately, MPI_Op_create takes a function that does not accept | ||
# a void* "thunk" parameter, making it impossible to fully simulate | ||
# a closure. So, we have to use a global variable instead. (Since | ||
# the reduction functions are barriers, being re-entrant is probably | ||
# not important in practice, fortunately.) For MPI_THREAD_MULTIPLE | ||
# using Julia native threading, however, we do make this global thread-local | ||
const _user_functions = Array(Function, 1) # resized to nthreads() at runtime | ||
const _user_op = Op(MPI_OP_NULL) # _mpi_user_function operation, initialized below | ||
|
||
# C callback function corresponding to MPI_User_function | ||
function _mpi_user_function(_a::Ptr{Void}, _b::Ptr{Void}, _len::Ptr{Cint}, t::Ptr{Cint}) | ||
len = unsafe_load(_len) | ||
T = mpitype_dict_inverse[unsafe_load(t)] | ||
a = Ptr{T}(_a) | ||
b = Ptr{T}(_b) | ||
f = _user_functions[threadid()] | ||
for i = 1:len | ||
unsafe_store!(b, f(unsafe_load(a,i), unsafe_load(b,i)), i) | ||
end | ||
return nothing | ||
end | ||
|
||
function user_op(opfunc::Function) | ||
# we must initialize these at runtime, but it can't be done in __init__ | ||
# since MPI.Init is not called yet. So we do it lazily here: | ||
if _user_op.val == MPI_OP_NULL | ||
# FIXME: to be thread-safe, there should really be a mutex lock | ||
# of some sort so that this initialization only occurs once. | ||
# To do when native threading in Julia stabilizes (and is documented). | ||
resize!(_user_functions, nthreads()) | ||
user_function = cfunction(_mpi_user_function, Void, (Ptr{Void}, Ptr{Void}, Ptr{Cint}, Ptr{Cint})) | ||
opnum = Ref{Cint}() | ||
ccall(MPI_OP_CREATE, Void, (Ptr{Void}, Ref{Cint}, Ref{Cint}, Ptr{Cint}), | ||
user_function, false, opnum, &0) | ||
_user_op.val = opnum[] | ||
end | ||
|
||
_user_functions[threadid()] = opfunc | ||
return _user_op | ||
end | ||
|
||
# use function types in Julia 0.5 to automatically use built-in | ||
# MPI operations for the corresponding Julia functions. | ||
if VERSION >= v"0.5.0-dev+2396" | ||
for (f,op) in ((+,SUM), (*,PROD), | ||
(min,MIN), (max,MAX), | ||
(&, BAND), (|, BOR), ($, BXOR)) | ||
@eval user_op(::$(typeof(f))) = $op | ||
end | ||
end | ||
|
||
Allreduce!{T}(sendbuf::MPIBuffertype{T}, recvbuf::MPIBuffertype{T}, | ||
count::Integer, opfunc::Function, comm::Comm) = | ||
Allreduce!(sendbuf, recvbuf, count, user_op(opfunc), comm) | ||
|
||
Reduce{T}(sendbuf::MPIBuffertype{T}, count::Integer, | ||
op::Function, root::Integer, comm::Comm) = | ||
Reduce(sendbuf, count, user_op(opfunc), root, comm) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters