-
-
Notifications
You must be signed in to change notification settings - Fork 121
/
onesided.jl
360 lines (302 loc) · 15.1 KB
/
onesided.jl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
mutable struct Win
val::MPI_Win
object
end
Base.:(==)(a::Win, b::Win) = a.val == b.val
Base.cconvert(::Type{MPI_Win}, win::Win) = win
Base.unsafe_convert(::Type{MPI_Win}, win::Win) = win.val
Base.unsafe_convert(::Type{Ptr{MPI_Win}}, win::Win) = convert(Ptr{MPI_Win}, pointer_from_objref(win))
const WIN_NULL = Win(API.MPI_WIN_NULL[], nothing)
add_load_time_hook!(() -> WIN_NULL.val = API.MPI_WIN_NULL[])
Win() = Win(WIN_NULL.val, nothing)
function free(win::Win)
if win != WIN_NULL && !Finalized()
# int MPI_Win_free(MPI_Win *win)
API.MPI_Win_free(win)
end
win.object = nothing
return nothing
end
mutable struct LockType
val::Cint
end
Base.:(==)(a::LockType, b::LockType) = a.val == b.val
Base.cconvert(::Type{Cint}, lock_type::LockType) = lock_type
Base.unsafe_convert(::Type{Cint}, lock_type::LockType) = lock_type.val
Base.unsafe_convert(::Type{Ptr{Cint}}, lock_type::LockType) = convert(Ptr{Cint}, pointer_from_objref(lock_type))
const LOCK_EXCLUSIVE = LockType(API.MPI_LOCK_EXCLUSIVE[])
const LOCK_SHARED = LockType(API.MPI_LOCK_SHARED[] )
add_load_time_hook!(() -> LOCK_EXCLUSIVE.val = API.MPI_LOCK_EXCLUSIVE[])
add_load_time_hook!(() -> LOCK_SHARED.val = API.MPI_LOCK_SHARED[] )
LockType(sym::Symbol) =
sym ≡ :exclusive ? LOCK_EXCLUSIVE :
sym ≡ :shared ? LOCK_SHARED :
error("Invalid LockType $sym")
"""
MPI.Win_create(base[, size::Integer, disp_unit::Integer], comm::Comm; infokws...)
Create a window over the array `base`, returning a `Win` object used by these
processes to perform RMA operations. This is a collective call over `comm`.
- `size` is the size of the window in bytes (default = `sizeof(base)`)
- `disp_unit` is the size of address scaling in bytes (default =
`sizeof(eltype(base))`)
- `infokws` are info keys providing optimization hints to the runtime.
[`MPI.free`](@ref) should be called on the `Win` object once operations have
been completed.
"""
function Win_create(base, size::Integer, disp_unit::Integer, comm::Comm; infokws...)
win = Win()
# int MPI_Win_create(void *base, MPI_Aint size, int disp_unit, MPI_Info info,
# MPI_Comm comm, MPI_Win *win)
API.MPI_Win_create(base, size, disp_unit, Info(infokws...), comm, win)
win.object = base
finalizer(free, win)
return win
end
function Win_create(base::Array{T}, comm::Comm; infokws...) where T
Win_create(base, sizeof(base), sizeof(T), comm; infokws...)
end
function Win_create(base::SubArray{T}, comm::Comm; infokws...) where T
Base.iscontiguous(base) || error("Array must be contiguous")
Win_create(base, sizeof(base), sizeof(T), comm; infokws...)
end
"""
win, array = MPI.Win_allocate_shared(Array{T}, dims, comm::Comm; infokws...)
Create and allocate a shared memory window for objects of type `T` of dimension
`dims` (either an integer or tuple of integers), returning a `Win` and the
`Array{T}` attached to the local process.
This is a collective call over `comm`, but `dims` can differ for each call (and
can be zero).
Use [`MPI.Win_shared_query`](@ref) to obtain the `Array` attached to a different
process in the same shared memory space.
`infokws` are info keys providing optimization hints.
[`MPI.free`](@ref) should be called on the `Win` object once operations have
been completed.
"""
function Win_allocate_shared(::Type{Ptr{T}}, len::Integer, comm::Comm; kwargs...) where T
win = Win()
out_baseptr = Ref{Ptr{T}}()
# int MPI_Win_allocate_shared(MPI_Aint size, int disp_unit, MPI_Info info,
# MPI_Comm comm, void *baseptr, MPI_Win *win)
API.MPI_Win_allocate_shared(len*sizeof(T), sizeof(T), Info(kwargs...), comm, out_baseptr, win)
finalizer(free, win)
return win, out_baseptr[]
end
function Win_allocate_shared(::Type{Array{T}}, dims, comm::Comm; kwargs...) where T
win, ptr = Win_allocate_shared(Ptr{T}, prod(dims), comm; kwargs...)
array = unsafe_wrap(Array, ptr, dims)
win.object = array
finalizer(free, win)
return win, array
end
"""
array = Win_shared_query(Array{T}, [dims,] win; rank)
Obtain the shared memory allocated by [`Win_allocate_shared`](@ref) of the
process `rank` in `win`. Returns an `Array{T}` of size `dims` (being a
`Vector{T}` if no `dims` argument is provided).
"""
Win_shared_query(::Type{Array{T}}, win::Win; rank) where {T} =
Win_shared_query(Array{T}, win, rank)
Win_shared_query(::Type{Ptr{T}}, win::Win; rank) where {T} =
Win_shared_query(Ptr{T}, win, rank)
Win_shared_query(::Type{Array{T}}, dims, win::Win; rank) where {T} =
Win_shared_query(Array{T}, dims, win, rank)
function Win_shared_query(::Type{Ptr{T}}, win::Win, owner_rank::Integer) where T
out_len = Ref{Cptrdiff_t}()
out_sizeT = Ref{Cint}()
out_baseptr = Ref{Ptr{T}}()
# int MPI_Win_shared_query(MPI_Win win, int rank, MPI_Aint *size,
# int *disp_unit, void *baseptr)
API.MPI_Win_shared_query(win, owner_rank, out_len, out_sizeT, out_baseptr)
out_len[], out_sizeT[], out_baseptr[]
end
function Win_shared_query(::Type{Array{T}}, win::Win, owner_rank::Integer) where T
len, sizeT, ptr = Win_shared_query(Ptr{T}, win, owner_rank)
sizeT == sizeof(T) || error("type sizes don't match")
# the ptr may be invalid for empty arrays, which will cause an error as unsafe_wrap
# checks the alignment of ptr, even for length 0
if len > 0
return unsafe_wrap(Array, ptr, div(len, sizeT))
else
return Array{T}(undef, 0)
end
end
Win_shared_query(::Type{Array{T}}, dims, win::Win, owner_rank::Integer) where T =
reshape(Win_shared_query(Array{T}, win, owner_rank), dims)
"""
MPI.Win_create_dynamic(comm::Comm; infokws...)
Create a dynamic window returning a `Win` object used by these processes to perform RMA operations
This is a collective call over `comm`.
`infokws` are info keys providing optimization hints.
[`MPI.free`](@ref) should be called on the `Win` object once operations have been completed.
"""
function Win_create_dynamic(comm::Comm; kwargs...)
win = Win()
# int MPI_Win_create_dynamic(MPI_Info info, MPI_Comm comm, MPI_Win *win)
API.MPI_Win_create_dynamic(Info(kwargs...), comm, win)
finalizer(free, win)
win.object = Set()
return win
end
function Win_attach!(win::Win, base::AbstractArray{T}) where T
# int MPI_Win_attach(MPI_Win win, void *base, MPI_Aint size)
API.MPI_Win_attach(win, base, sizeof(base))
push!(win.object, base)
end
function Win_detach!(win::Win, base::AbstractArray{T}) where T
# int MPI_Win_detach(MPI_Win win, const void *base)
API.MPI_Win_detach(win, base)
delete!(win.object, base)
end
Win_fence(assert::Integer, win::Win) = API.MPI_Win_fence(assert, win)
function Win_fence(win::Win; nostore=false, noput=false, noprecede=false, nosucceed=false)
assert =
(nostore * API.MPI_MODE_NOSTORE[]) |
(noput * API.MPI_MODE_NOPUT[]) |
(noprecede * API.MPI_MODE_NOPRECEDE[]) |
(nosucceed * API.MPI_MODE_NOSUCCEED[])
Win_fence(assert, win)
end
"""
Win_flush(win::Win; rank)
Completes all outstanding RMA operations initiated by the calling process to the
target rank on the specified window.
# External links
$(_doc_external("MPI_Win_flush"))
"""
Win_flush(win::Win; rank) = Win_flush(rank, win)
Win_flush(rank::Integer, win::Win) = API.MPI_Win_flush(rank, win)
Win_sync(win::Win) = API.MPI_Win_sync(win)
"""
Win_lock(win::Win; rank::Integer, type=:exclusive/:shared, nocheck=false)
Starts an RMA access epoch. The window at the process with rank `rank` can be
accessed by RMA operations on `win` during that epoch.
Multiple RMA access epochs (with calls to `MPI.Win_lock`) can occur
simultaneously; however, each access epoch must target a different process.
Accesses that are protected by an exclusive lock (`type=:exclusive`) will not be
concurrent at the window site with other accesses to the same window that are
lock protected. Accesses that are protected by a shared lock (`type=:shared`)
will not be concurrent at the window site with accesses protected by an
exclusive lock to the same window.
If `nocheck=true`, no other process holds, or will attempt to acquire, a
conflicting lock, while the caller holds the window lock. This is useful when
mutual exclusion is achieved by other means, but the coherence operations that
may be attached to the lock and unlock calls are still required.
# External links
$(_doc_external("MPI_Win_lock"))
"""
function Win_lock(win::Win; rank::Integer, type::Union{Symbol,LockType}, nocheck::Bool = false)
Win_lock(type isa Symbol ? LockType(type) : type, rank, nocheck * API.MPI_MODE_NOCHECK[], win)
end
function Win_lock(lock_type::LockType, rank::Integer, assert::Integer, win::Win)
# int MPI_Win_lock(int lock_type, int rank, int assert, MPI_Win win)
API.MPI_Win_lock(lock_type, rank, assert, win)
end
"""
Win_unlock(win::Win; rank::Integer)
Completes an RMA access epoch started by a call to [`Win_lock`](@ref).
# External links
$(_doc_external("MPI_Win_unlock"))
"""
Win_unlock(win::Win; rank::Integer) = Win_unlock(rank, win)
Win_unlock(rank::Integer, win::Win) = API.MPI_Win_unlock(rank, win)
# TODO: add some sort of "remote buffer": a way to specify different datatypes/counts
"""
Get!(origin, win::Win; rank::Integer, disp::Integer=0)
Copies data from the memory window `win` on the remote rank `rank`, with
displacement `disp`, into `origin` using remote memory access. `origin` can be a
[`Buffer`](@ref), or any object for which `Buffer(origin)` is defined.
# External links
$(_doc_external("MPI_Get"))
"""
Get!(origin, win::Win; rank::Integer, disp::Integer=0) =
Get!(origin, rank, disp, win)
function Get!(origin_buf::Buffer, target_rank::Integer, target_disp::Integer, win::Win)
# int MPI_Get(void *origin_addr, int origin_count,
# MPI_Datatype origin_datatype, int target_rank,
# MPI_Aint target_disp, int target_count,
# MPI_Datatype target_datatype, MPI_Win win)
API.MPI_Get(origin_buf.data, origin_buf.count, origin_buf.datatype,
target_rank, Cptrdiff_t(target_disp), origin_buf.count, origin_buf.datatype, win)
end
Get!(origin, target_rank::Integer, target_disp::Integer, win::Win) =
Get!(Buffer(origin), target_rank, target_disp, win)
Get!(origin, target_rank::Integer, win::Win) =
Get!(origin, target_rank, 0, win)
"""
Put!(origin, win::Win; rank::Integer, disp::Integer=0)
Copies data from `origin` into memory window `win` on remote rank `rank` at
displacement `disp` using remote memory access. `origin` can be a
[`Buffer`](@ref), or any object for which [`Buffer_send(origin)`](@ref) is
defined.
# External links
$(_doc_external("MPI_Put"))
"""
Put!(origin, win::Win; rank::Integer, disp::Integer=0) =
Put!(origin, rank, disp, win)
function Put!(origin_buf::Buffer, target_rank::Integer, target_disp::Integer, win::Win)
# int MPI_Put(const void *origin_addr, int origin_count,
# MPI_Datatype origin_datatype, int target_rank,
# MPI_Aint target_disp, int target_count,
# MPI_Datatype target_datatype, MPI_Win win)
API.MPI_Put(origin_buf.data, origin_buf.count, origin_buf.datatype,
target_rank, Cptrdiff_t(target_disp), origin_buf.count, origin_buf.datatype, win)
end
Put!(origin, target_rank::Integer, target_disp::Integer, win::Win) =
Put!(Buffer_send(origin), target_rank, target_disp, win)
Put!(origin, target_rank::Integer, win::Win) =
Put!(origin, target_rank, 0, win)
Fetch_and_op!(source, returnval, op, win::Win; rank::Integer, disp::Integer=0) =
Fetch_and_op!(source, returnval, rank, disp, op, win)
function Fetch_and_op!(sourceval::Ref{T}, returnval::Ref{T}, target_rank::Integer, target_disp::Integer, op::Op, win::Win) where {T}
# int MPI_Fetch_and_op(const void *origin_addr, void *result_addr,
# MPI_Datatype datatype, int target_rank, MPI_Aint target_disp,
# MPI_Op op, MPI_Win win)
API.MPI_Fetch_and_op(sourceval, returnval, Datatype(T), target_rank, target_disp, op, win)
end
"""
Accumulate!(origin, op, win::Win; rank::Integer, disp::Integer=0)
Combine the content of the `origin` buffer into the target buffer (specified by `win` and
displacement `target_disp`) with reduction operator `op` on the remote rank
`target_rank` using remote memory access.
`origin` can be a [`Buffer`](@ref), or any object for which [`Buffer_send(origin)`](@ref) is defined.
`op` can be any predefined [`Op`](@ref) (custom operators are not supported).
# External links
$(_doc_external("MPI_Accumulate"))
"""
Accumulate!(origin, op, win::Win; rank::Integer, disp::Integer=0) =
Accumulate!(origin, rank, disp, op, win)
function Accumulate!(origin_buf::Buffer, target_rank::Integer, target_disp::Integer, op::Op, win::Win)
# int MPI_Accumulate(const void *origin_addr, int origin_count,
# MPI_Datatype origin_datatype, int target_rank,
# MPI_Aint target_disp, int target_count,
# MPI_Datatype target_datatype, MPI_Op op, MPI_Win win)
API.MPI_Accumulate(origin_buf.data, origin_buf.count, origin_buf.datatype,
target_rank, Cptrdiff_t(target_disp), origin_buf.count, origin_buf.datatype, op, win)
end
Accumulate!(origin, target_rank::Integer, target_disp::Integer, op::Op, win::Win) =
Accumulate!(Buffer_send(origin), target_rank, target_disp, op, win)
"""
Get_accumulate!(origin, result, target_rank::Integer, target_disp::Integer, op::Op, win::Win)
Combine the content of the `origin` buffer into the target buffer (specified by `win` and
displacement `target_disp`) with reduction operator `op` on the remote
rank `target_rank` using remote memory access. `Get_accumulate` also returns the
content of the target buffer _before_ accumulation into the `result` buffer.
`origin` can be a [`Buffer`](@ref), or any object for which `Buffer_send(origin)` is defined, `result` can be a [`Buffer`](@ref), or any object for which `Buffer(result)` is defined.
`op` can be any predefined [`Op`](@ref) (custom operators are not supported).
# External links
$(_doc_external("MPI_Get_accumulate"))
"""
Get_accumulate!(origin, result, op, win::Win; rank::Integer, disp::Integer=0) =
Get_accumulate!(origin, result, rank, disp, op, win)
function Get_accumulate!(origin_buf::Buffer, result_buf::Buffer, target_rank::Integer, target_disp::Integer, op::Op, win::Win)
# int MPI_Get_accumulate(const void *origin_addr, int origin_count,
# MPI_Datatype origin_datatype, void *result_addr,
# int result_count, MPI_Datatype result_datatype,
# int target_rank, MPI_Aint target_disp, int target_count,
# MPI_Datatype target_datatype, MPI_Op op, MPI_Win win)
API.MPI_Get_accumulate(origin_buf.data, origin_buf.count, origin_buf.datatype,
result_buf.data, result_buf.count, result_buf.datatype,
target_rank, Cptrdiff_t(target_disp), origin_buf.count, origin_buf.datatype, op, win)
end
Get_accumulate!(origin, result, target_rank::Integer, target_disp::Integer, op::Op, win::Win) =
Get_accumulate!(Buffer_send(origin), Buffer(result), target_rank, target_disp, op, win)