Skip to content

Commit ac4ed7a

Browse files
authored
Use consts for known ABIs (#361)
Adds a mechanism to detect the ABI via MPI_LIBRARY_VERSION_STRING, and use that to detect the appropriate consts file, falling back on the C generator only when that fails. Other changes: - add to Travis test matrix for C generator fallback - makes `MPI_Comm` etc aliases for `Cint` or `Ptr{Cvoid}` instead of custom types - defines `mpiexec` function consistent with how BinaryBuilder packages wrap executables, and adds support for `JULIA_MPIEXEC_ARGS` everywhere - `--oversubscribe` is no longer always applied in `runtests.jl` (use `JULIA_MPIEXEC_ARGS` instead). - `JULIA_MPIEXEC_ARGS` uses `Base.shell_split` instead of plain `split` to deal with quoted strings
1 parent cd43f8b commit ac4ed7a

22 files changed

+617
-235
lines changed

.travis.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,13 @@ cache:
2929
env:
3030
- MPI_IMPL=mpich
3131
- MPI_IMPL=openmpi
32+
JULIA_MPIEXEC_ARGS="--oversubscribe"
3233
- MPI_IMPL=intelmpi
34+
- MPI_IMPL=mpich
35+
JULIA_MPI_ABI=UnknownABI
36+
- MPI_IMPL=openmpi
37+
JULIA_MPI_ABI=UnknownABI
38+
JULIA_MPIEXEC_ARGS="--oversubscribe"
3339

3440
matrix:
3541
allow_failures: # issue 262

deps/build.jl

Lines changed: 18 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
1-
using Libdl
1+
_doc_external(fname) = ""
22

3+
include(joinpath("..","src","paths.jl"))
4+
include(joinpath("..","src","implementations.jl"))
5+
6+
if MPI_LIBRARY_ABI != UnknownABI
7+
# constants provided under src/consts/
8+
exit(0)
9+
end
10+
11+
@info "Uknown MPI ABI: building constants file"
312

413
MPI_PATH = get(ENV, "JULIA_MPI_PATH", nothing)
514
MPI_LIBRARY_PATH = get(ENV, "JULIA_MPI_LIBRARY_PATH") do
@@ -8,6 +17,7 @@ end
817
MPI_INCLUDE_PATH = get(ENV, "JULIA_MPI_INCLUDE_PATH") do
918
MPI_PATH !== nothing ? joinpath(MPI_PATH,"include") : nothing
1019
end
20+
1121
mpicc = get(ENV, "JULIA_MPICC") do
1222
if MPI_PATH !== nothing
1323
joinpath(MPI_PATH,"bin","mpicc")
@@ -16,27 +26,6 @@ mpicc = get(ENV, "JULIA_MPICC") do
1626
end
1727
end
1828

19-
const mpiexec_path = get(ENV, "JULIA_MPIEXEC") do
20-
if MPI_PATH !== nothing && Sys.isexecutable(joinpath(MPI_PATH,"bin","mpiexec"))
21-
joinpath(MPI_PATH,"bin","mpiexec")
22-
else
23-
Sys.which("mpiexec")
24-
end
25-
end
26-
27-
const mpiexec_args = split(get(ENV, "JULIA_MPIEXEC_ARGS", ""))
28-
29-
const libmpi = get(ENV, "JULIA_MPI_LIBRARY") do
30-
libmpi = find_library(["libmpi", "libmpi_ibm", "msmpi", "libmpich"],
31-
MPI_LIBRARY_PATH !== nothing ? [MPI_LIBRARY_PATH] : [])
32-
if libmpi == ""
33-
error("No MPI library found.\nEnsure an MPI implementation is loaded, or set the `JULIA_MPI_PATH` variable.")
34-
end
35-
# expand paths
36-
dlpath(libmpi)
37-
end
38-
39-
4029
if haskey(ENV, "JULIA_MPI_CFLAGS")
4130
CFLAGS = split(ENV["JULIA_MPI_CFLAGS"])
4231
else
@@ -53,65 +42,17 @@ else
5342
end
5443
end
5544

56-
libsize = filesize(libmpi)
57-
58-
@info "Using MPI library $libmpi"
59-
60-
function Get_version()
61-
major = Ref{Cint}()
62-
minor = Ref{Cint}()
63-
if Sys.iswindows()
64-
ccall((:MPI_Get_version, libmpi), stdcall, Cint,
65-
(Ptr{Cint}, Ptr{Cint}), major, minor)
66-
else
67-
ccall((:MPI_Get_version, libmpi), Cint,
68-
(Ptr{Cint}, Ptr{Cint}), major, minor)
69-
end
70-
VersionNumber(major[], minor[])
71-
end
72-
73-
MPI_VERSION = Get_version()
74-
75-
@info "MPI version:\n$(MPI_VERSION)"
76-
77-
function Get_library_version()
78-
# There is no way to query at runtime what the length of the buffer should be.
79-
# https://github.com/mpi-forum/mpi-issues/issues/159
80-
# 8192 is the maximum value of MPI_MAX_LIBRARY_VERSION_STRING across known
81-
# implementations.
82-
buf = Array{UInt8}(undef, 8192)
83-
buflen = Ref{Cint}()
84-
if Sys.iswindows()
85-
ccall((:MPI_Get_library_version, libmpi), stdcall, Cint, (Ptr{UInt8}, Ref{Cint}), buf, buflen)
86-
else
87-
ccall((:MPI_Get_library_version, libmpi), Cint, (Ptr{UInt8}, Ref{Cint}), buf, buflen)
88-
end
89-
resize!(buf, buflen[])
90-
return String(buf)
91-
end
45+
include("gen_consts.jl")
9246

93-
MPI_LIBRARY_VERSION_STRING = Get_library_version()
47+
run(`$mpicc gen_consts.c -o gen_consts $CFLAGS`)
9448

95-
@info "MPI library version:\n$(MPI_LIBRARY_VERSION_STRING)"
96-
97-
open("deps.jl","w") do f
49+
open("consts.jl","w") do f
9850
println(f, "# This file is automatically generated")
9951
println(f, "# Do not edit")
10052
println(f)
101-
println(f, :(const libmpi = $libmpi))
102-
println(f, :(const libmpi_size = $libsize))
103-
println(f, :(const MPI_VERSION = $MPI_VERSION))
104-
println(f, :(const MPI_LIBRARY_VERSION_STRING = $MPI_LIBRARY_VERSION_STRING))
105-
println(f, :(const mpiexec_path = $mpiexec_path))
106-
107-
if Sys.iswindows()
108-
println(f, :(include("consts_msmpi.jl")))
109-
else
110-
include("gen_consts.jl")
53+
println(f, :(MPI_LIBRARY_VERSION_STRING == $MPI_LIBRARY_VERSION_STRING || error("MPI library changed, re-run Pkg.build(\"MPI\")")))
54+
println(f)
55+
end
11156

112-
run(`$mpicc gen_consts.c -o gen_consts $CFLAGS`)
113-
run(`$mpiexec_path $mpiexec_args -n 1 ./gen_consts`)
57+
mpiexec(cmd -> run(`$cmd -n 1 ./gen_consts`))
11458

115-
println(f, :(include("consts.jl")))
116-
end
117-
end

deps/gen_consts.jl

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -133,10 +133,8 @@ int main(int argc, char *argv[]) {
133133
MPI_Init(&argc, &argv);
134134
135135
FILE *fptr;
136-
fptr = fopen("consts.jl", "w");
136+
fptr = fopen("consts.jl", "a");
137137
138-
fprintf(fptr, "# This file is automatically generated\\n");
139-
fprintf(fptr, "# Do not edit\\n");
140138
""")
141139

142140
println(f," fprintf(fptr, \"const MPI_Aint = Int%d\\n\", 8*(int)sizeof(MPI_Aint));")
@@ -149,17 +147,16 @@ int main(int argc, char *argv[]) {
149147
println(f," fprintf(fptr, \"const MPI_Status_Error_offset = %d\\n\", (int)offsetof(MPI_Status, MPI_ERROR));")
150148

151149
for (T,constants) in MPI_handle
152-
println(f," fprintf(fptr, \"primitive type $T %d end\\n\", (int)(sizeof($T) * 8));")
153-
154150
T_f2c = T == :MPI_Datatype ? :MPI_Type_f2c : Symbol(T, :_f2c)
155151
T_c2f = T == :MPI_Datatype ? :MPI_Type_c2f : Symbol(T, :_c2f)
156152
if Libdl.dlsym_e(libptr, T_f2c) == C_NULL
157-
println(f," fprintf(fptr, \"$T(c::Cint) = reinterpret($T,c)\\n\");")
153+
println(f," fprintf(fptr, \"const $T = Cint\\n\");")
158154
for constant in constants
159155
println(f," fprintf(fptr, \"const $constant = Cint(%i)\\n\", $constant);")
160156
end
161157
else
162-
println(f," fprintf(fptr, \"$T(c::Cint) = ccall((:$T_f2c,libmpi),$T,(Cint,),c)\\n\");")
158+
println(f," fprintf(fptr, \"const $T = Ptr{Cvoid}\\n\");")
159+
println(f," fprintf(fptr, \"$(Symbol(T,:_f2c))(c::Cint) = ccall((:$T_f2c,libmpi),$T,(Cint,),c)\\n\");")
163160
for constant in constants
164161
println(f," fprintf(fptr, \"const $constant = Cint(%i)\\n\", $T_c2f($constant));")
165162
end

docs/make.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ makedocs(
4646
"usage.md",
4747
"Examples" => EXAMPLES,
4848
"Reference" => [
49+
"library.md",
4950
"environment.md",
5051
"comm.md",
5152
"pointtopoint.md",

docs/src/environment.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# Environment
22

3+
## Launching MPI programs
4+
5+
```@docs
6+
mpiexec
7+
```
8+
39
## Functions
410

511
```@docs
@@ -9,5 +15,4 @@ MPI.Initialized
915
MPI.Finalize
1016
MPI.Finalized
1117
MPI.universe_size
12-
MPI.has_cuda
1318
```

docs/src/installation.md

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,39 +2,39 @@
22

33
## Requirements
44

5-
### Unix systems (Linux and MacOS)
5+
MPI.jl requires a shared library installation of a C MPI library, supporting the MPI 3.0 standard or later.
66

7-
MPI.jl requires:
7+
## Building
8+
9+
The MPI.jl package can be installed via `add MPI` in the [Julia package
10+
manager](https://docs.julialang.org/en/v1/stdlib/Pkg/index.html). The package will attempt
11+
to find and identify the MPI installation.
812

9-
- A shared library MPI installation for C (supporting MPI standard 3.0), and
10-
- A C compiler available via the `mpicc` command: this is required as part of the build
11-
process to determine the necessary type definitions and constants.
13+
The MPI standard doesn't specify the exact application binary interface (ABI), but the
14+
following implementations should work directly:
1215

13-
This has been tested with:
1416
- [Open MPI](http://www.open-mpi.org/)
15-
- [MPICH](http://www.mpich.org/)
17+
- [MPICH](http://www.mpich.org/) (v3.1 or later)
1618
- [Intel MPI](https://software.intel.com/en-us/mpi-library)
19+
- [Microsoft MPI](https://docs.microsoft.com/en-us/message-passing-interface/microsoft-mpi)
20+
- [IBM Spectrum MPI](https://www.ibm.com/us-en/marketplace/spectrum-mpi)
1721

18-
### Windows
19-
20-
MPI.jl requires the [Microsoft MPI (MS-MPI)](https://docs.microsoft.com/en-us/message-passing-interface/microsoft-mpi) runtime to be installed.
22+
For other implementations, the build script will attempt to build a small C program to
23+
determine the appropriate type definitions and constants. This requires a compatible C
24+
compiler (`mpicc` by default).
2125

22-
## Building
23-
24-
The MPI.jl package can be installed via `add MPI` in the [Julia package manager](https://docs.julialang.org/en/v1/stdlib/Pkg/index.html).
26+
## Environment variables
2527

26-
The build script will attempt to find the shared library and constants: this can be
27-
controlled with the optional environment variables:
28+
The following optional environment variables can be used to control certain aspects of the
29+
build script and other library behaviour:
2830

31+
- `JULIA_MPI_ABI`: the application binary interface, a string matching an [`MPIABI`](@ref) value.
2932
- `JULIA_MPI_PATH`: the top-level installation directory of MPI.
3033
- `JULIA_MPI_LIBRARY`: the path of the MPI shared library.
3134
- `JULIA_MPI_LIBRARY_PATH`: the directory containing the MPI library files.
3235
- `JULIA_MPI_INCLUDE_PATH`: the directory containing the MPI header files.
3336
- `JULIA_MPI_CFLAGS`: C flags passed to the constant generation build (default: `-lmpi`)
3437
- `JULIA_MPICC`: MPI C compiler (default: `mpicc`)
3538
- `JULIA_MPIEXEC`: MPI launcher command (default: `mpiexec`)
36-
- `JULIA_MPIEXEC_ARGS`: Additional arguments to be passed to MPI launcher (only used in the build step and tests).
39+
- `JULIA_MPIEXEC_ARGS`: Additional arguments to be passed to MPI launcher.
3740
- `JULIA_MPI_HAS_CUDA`: override the [`MPI.has_cuda`](@ref) function.
38-
39-
If your MPI installation changes (e.g. it is upgraded by the system, or you switch
40-
libraries), you will need to re-run `build MPI` at the package prompt.

docs/src/library.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Library information
2+
3+
## Constants
4+
5+
```@docs
6+
MPI.MPI_VERSION
7+
MPI.MPI_LIBRARY
8+
MPI.MPI_LIBRARY_VERSION
9+
MPI.MPI_LIBRARY_ABI
10+
MPI.MPI_LIBRARY_VERSION_STRING
11+
```
12+
13+
## Enums
14+
15+
```@docs
16+
MPI.MPIImpl
17+
MPI.MPIABI
18+
```
19+
20+
## Functions
21+
22+
```@docs
23+
MPI.has_cuda
24+
```

docs/src/usage.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ Hello world, I am rank 2 of 3
2626
Hello world, I am rank 1 of 3
2727
```
2828

29+
The [`mpiexec`](@ref) function is provided for launching MPI programs from Julia itself.
30+
2931
## CUDA-aware MPI support
3032

3133
If your MPI implementation has been compiled with CUDA support, then `CuArray`s (from the

src/MPI.jl

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ using Libdl, Serialization
44
using Requires
55
using DocStringExtensions
66

7+
export mpiexec
78

89
function serialize(x)
910
s = IOBuffer()
@@ -31,7 +32,19 @@ function _doc_external(fname)
3132
"""
3233
end
3334

34-
include(joinpath(@__DIR__, "..", "deps", "deps.jl"))
35+
include("paths.jl")
36+
include("implementations.jl")
37+
38+
if MPI_LIBRARY_ABI == UnknownABI
39+
include(joinpath(@__DIR__, "..", "deps", "consts.jl"))
40+
elseif MPI_LIBRARY_ABI == MPICHABI
41+
include(joinpath("consts", "mpich.jl"))
42+
elseif MPI_LIBRARY_ABI == OpenMPIABI
43+
include(joinpath("consts", "openmpi.jl"))
44+
elseif MPI_LIBRARY_ABI == MicrosoftMPIABI
45+
include(joinpath("consts", "microsoftmpi.jl"))
46+
end
47+
3548
include("error.jl")
3649
include("handle.jl")
3750
include("info.jl")
@@ -49,24 +62,30 @@ include("io.jl")
4962
include("deprecated.jl")
5063

5164
function __init__()
65+
5266
@static if Sys.isunix()
5367
# need to open libmpi with RTLD_GLOBAL flag for Linux, before
5468
# any ccall cannot use RTLD_DEEPBIND; this leads to segfaults
5569
# at least on Ubuntu 15.10
5670
Libdl.dlopen(libmpi, Libdl.RTLD_LAZY | Libdl.RTLD_GLOBAL)
5771
end
5872

73+
if MPI_LIBRARY_VERSION_STRING != Get_library_version()
74+
# MPI library has changed, invalidate cache
75+
cachefile = Base.compilecache(Base.PkgId(MPI))
76+
rm(cachefile)
77+
# TODO: figure out if we can reload package without erroring
78+
# though that would probably trigger a race condition
79+
error("MPI library has changed, please restart Julia")
80+
end
81+
5982
# disable UCX memory hooks since it can mess up dlopen
6083
# https://github.com/openucx/ucx/issues/4001
6184
ENV["UCX_MEM_MMAP_RELOC"] = "no"
6285
ENV["UCX_MEM_MALLOC_HOOKS"] = "no"
6386
ENV["UCX_MEM_MALLOC_RELOC"] = "no"
6487
ENV["UCX_MEM_EVENTS"] = "no"
6588

66-
if filesize(dlpath(libmpi)) != libmpi_size
67-
error("MPI library has changed, re-run Pkg.build(\"MPI\")")
68-
end
69-
7089
@require CuArrays="3a865a2d-5b23-5a0f-bc46-62713ec82fae" include("cuda.jl")
7190
end
7291

0 commit comments

Comments
 (0)