Skip to content

Commit d189440

Browse files
authored
Additional threading functions and docs (#364)
Expose `MPI_Query_thread` and `MPI_Is_thread_main`, along with some doc cleanup.
1 parent 84efa98 commit d189440

File tree

8 files changed

+106
-32
lines changed

8 files changed

+106
-32
lines changed

docs/make.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ for (example_title, example_md) in EXAMPLES
3333
end
3434
end
3535

36+
DocMeta.setdocmeta!(MPI, :DocTestSetup, :(using MPI); recursive=true)
3637

3738
makedocs(
3839
sitename = "MPI.jl",

docs/src/environment.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,20 @@
66
mpiexec
77
```
88

9+
## Enums
10+
11+
```@docs
12+
MPI.ThreadLevel
13+
```
14+
915
## Functions
1016

1117
```@docs
1218
MPI.Abort
1319
MPI.Init
1420
MPI.Init_thread
21+
MPI.Query_thread
22+
MPI.Is_thread_main
1523
MPI.Initialized
1624
MPI.Finalize
1725
MPI.Finalized

docs/src/installation.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,12 @@ For other implementations, the build script will attempt to build a small C prog
2323
determine the appropriate type definitions and constants. This requires a compatible C
2424
compiler (`mpicc` by default).
2525

26-
## Environment variables
26+
## [Environment variables](@id environment_variables)
2727

2828
The following optional environment variables can be used to control certain aspects of the
2929
build script and other library behaviour:
3030

31-
- `JULIA_MPI_ABI`: the application binary interface, a string matching an [`MPIABI`](@ref) value.
31+
- `JULIA_MPI_ABI`: the application binary interface, a string matching an [`MPI.MPIABI`](@ref) value.
3232
- `JULIA_MPI_PATH`: the top-level installation directory of MPI.
3333
- `JULIA_MPI_LIBRARY`: the path of the MPI shared library.
3434
- `JULIA_MPI_LIBRARY_PATH`: the directory containing the MPI library files.

docs/src/library.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,5 @@ MPI.MPIABI
2121

2222
```@docs
2323
MPI.has_cuda
24+
MPI.identify_implementation
2425
```

docs/src/topology.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ MPI.Cart_coords!
66
MPI.Cart_create
77
MPI.Cart_get
88
MPI.Cart_rank
9+
MPI.Cart_shift
910
MPI.Cart_sub
1011
MPI.Cartdim_get
1112
MPI.Dims_create!

src/environment.jl

Lines changed: 63 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -55,30 +55,46 @@ function Init()
5555
end
5656
end
5757

58+
"""
59+
ThreadLevel
60+
61+
An Enum denoting the level of threading support in the current process:
62+
63+
- `MPI.THREAD_SINGLE`: Only one thread will execute.
64+
65+
- `MPI.THREAD_FUNNELED`: The process may be multi-threaded, but the application must
66+
ensure that only the main thread makes MPI calls. See [`Is_thread_main`](@ref).
67+
68+
- `MPI.THREAD_SERIALIZED`: The process may be multi-threaded, and multiple threads may
69+
make MPI calls, but only one at a time (i.e. all MPI calls are serialized).
70+
71+
- `MPI.THREAD_MULTIPLE`: Multiple threads may call MPI, with no restrictions.
72+
73+
# See also
74+
75+
- [`Init_thread`](@ref)
76+
- [`Query_thread`](@ref)
77+
"""
5878
@enum ThreadLevel begin
5979
THREAD_SINGLE = MPI_THREAD_SINGLE
6080
THREAD_FUNNELED = MPI_THREAD_FUNNELED
6181
THREAD_SERIALIZED = MPI_THREAD_SERIALIZED
6282
THREAD_MULTIPLE = MPI_THREAD_MULTIPLE
6383
end
64-
84+
6585

6686
"""
6787
Init_thread(required::ThreadLevel)
6888
6989
Initialize MPI and the MPI thread environment in the current process. The argument
70-
specifies the required thread level, which is one of the following:
90+
specifies the required level of threading support, see [`ThreadLevel`](@ref).
7191
72-
- `MPI.THREAD_SINGLE`: Only one thread will execute.
73-
- `MPI.THREAD_FUNNELED`: The process may be multi-threaded, but the application must ensure that only the main thread makes MPI calls.
74-
- `MPI.THREAD_SERIALIZED`: The process may be multi-threaded, and multiple threads may make MPI calls, but only one at a time (i.e. all MPI calls are serialized).
75-
- `MPI.THREAD_MULTIPLE`: Multiple threads may call MPI, with no restrictions.
92+
The function will return the provided `ThreadLevel`, and values may be compared via
93+
inequalities, i.e.
7694
77-
Tne function will return the provided `ThreadLevel`, and values may be compared via inequalities, i.e.
7895
```julia
79-
if Init_thread(required) < required
80-
error("Insufficient threading")
81-
end
96+
provided = Init_thread(required)
97+
@assert provided >= required
8298
```
8399
84100
All MPI programs must contain exactly one call to [`MPI.Init`](@ref) or
@@ -102,7 +118,7 @@ function Init_thread(required::ThreadLevel)
102118
if provided < required
103119
@warn "Thread level requested = $required, provided = $provided"
104120
end
105-
121+
106122
REFCOUNT[] = 1
107123
atexit(refcount_dec)
108124

@@ -112,6 +128,41 @@ function Init_thread(required::ThreadLevel)
112128
return provided
113129
end
114130

131+
"""
132+
Query_thread()
133+
134+
Query the level of threading support in the current process.
135+
Returns a [`ThreadLevel`](@ref) value denoting
136+
137+
# External links
138+
$(_doc_external("MPI_Query_thread"))
139+
"""
140+
function Query_thread()
141+
r_provided = Ref{ThreadLevel}()
142+
143+
# int MPI_Query_thread(int *provided)
144+
@mpichk ccall((:MPI_Query_thread, libmpi), Cint,
145+
(Ref{ThreadLevel},), r_provided)
146+
return r_provided[]
147+
end
148+
149+
"""
150+
Is_thread_main()
151+
152+
Queries whether the current thread is the main thread according to MPI. This can be called
153+
by any thread, and is useful for the `THREAD_FUNNELED` [`ThreadLevel`](@ref).
154+
155+
# External links
156+
$(_doc_external("MPI_Is_thread_main"))
157+
"""
158+
function Is_thread_main()
159+
r_flag = Ref{Cint}()
160+
# int MPI_Is_thread_main(int *flag)
161+
@mpichk ccall((:MPI_Is_thread_main, libmpi), Cint,
162+
(Ref{Cint},), r_flag)
163+
return r_flag[] != 0
164+
end
165+
115166

116167
"""
117168
Finalize()
@@ -207,7 +258,7 @@ function Wtick()
207258
ccall((:MPI_Wtick, libmpi), stdcall, Cdouble, ())
208259
else
209260
ccall((:MPI_Wtick, libmpi), Cdouble, ())
210-
end
261+
end
211262
end
212263

213264
function Wtime()

src/implementations.jl

Lines changed: 24 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,19 @@
11

22
function Get_library_version()
3-
# There is no way to query at runtime what the length of the buffer should be.
4-
# https://github.com/mpi-forum/mpi-issues/issues/159
3+
# There is no way to query at runtime what the length of the buffer should be.
4+
# https://github.com/mpi-forum/mpi-issues/issues/159
55
# 8192 is the maximum value of MPI_MAX_LIBRARY_VERSION_STRING across known
66
# implementations.
77
buf = Array{UInt8}(undef, 8192)
88
buflen = Ref{Cint}()
99

10-
# Microsoft MPI uses stdcall calling convention
11-
libfilename, = split(basename(libmpi),'.')
10+
# Microsoft MPI uses stdcall calling convention
11+
libfilename, = split(basename(libmpi),'.')
1212
if libfilename == "msmpi"
1313
ccall((:MPI_Get_library_version, libmpi), stdcall, Cint, (Ptr{UInt8}, Ref{Cint}), buf, buflen)
1414
else
1515
ccall((:MPI_Get_library_version, libmpi), Cint, (Ptr{UInt8}, Ref{Cint}), buf, buflen)
16-
end
16+
end
1717
resize!(buf, buflen[])
1818
return String(buf)
1919
end
@@ -60,10 +60,15 @@ end
6060
6161
An enum corresponding to known MPI Application Binary Interfaces (ABI)
6262
63-
- `UnknownABI`: unable to determine MPI ABI. This
64-
- `MPICHABI`: Compatible with [MPICH ABI Compatibility Initiative](https://www.mpich.org/abi/).
65-
- `OpenMPIABI`: Compatible with Open MPI
66-
- `MicrosftMPIABI`: Compatible with Microsoft MPI
63+
- `UnknownABI`: unable to determine MPI ABI. MPI.jl will attempt to build a small C
64+
program to determine the necessary constants and type information.
65+
66+
- `MPICHABI`: Compatible with [MPICH ABI Compatibility
67+
Initiative](https://www.mpich.org/abi/).
68+
69+
- `OpenMPIABI`: Compatible with [Open MPI](https://www.open-mpi.org/).
70+
71+
- `MicrosftMPIABI`: Compatible with [Microsoft MPI](https://docs.microsoft.com/en-us/message-passing-interface/microsoft-mpi).
6772
"""
6873
@enum MPIABI begin
6974
UnknownABI
@@ -80,7 +85,10 @@ Attempt to identify the MPI implementation based on
8085
8186
- `impl`: a value of type [`MPIImpl`](@ref)
8287
- `version`: a `VersionNumber` of the library, or `nothing` if it cannot be determined.
83-
- `abi`: a value of [`MPIABI`](@ref). This can be overridden by the `JULIA_MPI_ABI` environment variable.
88+
- `abi`: a value of [`MPIABI`](@ref). This can be overridden by the `JULIA_MPI_ABI` [environment variable](@ref environment_variables).
89+
90+
This function is only intended for internal use. Users should use [`MPI_LIBRARY`](@ref),
91+
[`MPI_LIBRARY_VERSION`](@ref) or [`MPI_LIBRARY_ABI`](@ref).
8492
"""
8593
function identify_implementation()
8694
impl = UnknownMPI
@@ -95,7 +103,7 @@ function identify_implementation()
95103
if version >= v"3.1"
96104
abi = MPICHABI
97105
end
98-
end
106+
end
99107

100108
elseif startswith(MPI_LIBRARY_VERSION_STRING, "Open MPI")
101109
# Open MPI / Spectrum MPI
@@ -107,7 +115,7 @@ function identify_implementation()
107115
abi = OpenMPIABI
108116

109117
elseif startswith(MPI_LIBRARY_VERSION_STRING, "Microsoft MPI")
110-
impl = MicrosoftMPI
118+
impl = MicrosoftMPI
111119
# "Microsoft MPI %u.%u.%u.%u%S"
112120
# ignore last 2 (build numbers)
113121
if (m = match(r"^Microsoft MPI v(\d+.\d+)", MPI_LIBRARY_VERSION_STRING)) !== nothing
@@ -118,7 +126,7 @@ function identify_implementation()
118126
elseif startswith(MPI_LIBRARY_VERSION_STRING, "Intel")
119127
impl = IntelMPI
120128

121-
# TODO: figure out how to parse
129+
# TODO: figure out how to parse
122130
# "Intel(R) MPI Library 2019 Update 4 for Linux* OS"
123131
if (m = match(r"^Intel\(R\) MPI Library (\d+)", MPI_LIBRARY_VERSION_STRING)) !== nothing
124132
version = VersionNumber(m.captures[1])
@@ -141,11 +149,11 @@ function identify_implementation()
141149
if (abienv = get(ENV, "JULIA_MPI_ABI", nothing)) !== nothing
142150
for inst in instances(MPIABI)
143151
if String(Symbol(inst)) == abienv
144-
abi = inst
152+
abi = inst
145153
end
146154
end
147155
end
148-
156+
149157
return impl, version, abi
150158
end
151159

@@ -154,7 +162,7 @@ const MPI_LIBRARY, MPI_LIBRARY_VERSION, MPI_LIBRARY_ABI = identify_implementatio
154162
"""
155163
MPI_LIBRARY :: MPIImpl
156164
157-
The current MPI implementation: this is determined by
165+
The current MPI implementation: this is determined by
158166
159167
# See also
160168
- [`MPIImpl`](@ref)

test/test_threads.jl

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ end
1010

1111
provided = MPI.Init_thread(MPI.THREAD_MULTIPLE)
1212

13+
@test MPI.THREAD_SINGLE <= provided <= MPI.THREAD_MULTIPLE
14+
@test MPI.Query_thread() == provided
15+
@test MPI.Is_thread_main()
16+
1317
comm = MPI.COMM_WORLD
1418
size = MPI.Comm_size(comm)
1519
rank = MPI.Comm_rank(comm)
@@ -24,7 +28,7 @@ if provided == MPI.THREAD_MULTIPLE
2428
recv_arr = zeros(N)
2529

2630
reqs = Array{MPI.Request}(undef, 2N)
27-
31+
2832
Threads.@threads for i = 1:N
2933
reqs[N+i] = MPI.Irecv!(@view(recv_arr[i:i]), src, i, comm)
3034
reqs[i] = MPI.Isend(@view(send_arr[i:i]), dst, i, comm)
@@ -33,6 +37,6 @@ if provided == MPI.THREAD_MULTIPLE
3337
MPI.Waitall!(reqs)
3438

3539
@test recv_arr == send_arr
36-
end
40+
end
3741

3842
MPI.Finalize()

0 commit comments

Comments
 (0)