Skip to content

Commit

Permalink
Merge branch 'master' into vc/gitlab
Browse files Browse the repository at this point in the history
  • Loading branch information
simonbyrne committed Aug 12, 2019
2 parents 1607022 + 33e4253 commit 5eb7607
Show file tree
Hide file tree
Showing 25 changed files with 142 additions and 556 deletions.
5 changes: 2 additions & 3 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
language: julia
sudo: required
dist: trusty
dist: xenial

os:
- linux
Expand Down Expand Up @@ -66,8 +65,8 @@ jobs:
script:
- export DOCUMENTER_DEBUG="true"
- julia --color=yes --project=docs/ -e 'using Pkg;
Pkg.instantiate();
Pkg.develop(PackageSpec(path=pwd()));
Pkg.instantiate();
Pkg.build()'
- julia --color=yes --project=docs/ docs/make.jl
- stage: "Coverage"
Expand Down
1 change: 1 addition & 0 deletions docs/.gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
build/
site/
src/examples/
6 changes: 6 additions & 0 deletions docs/examples/01-hello.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
using MPI
MPI.Init()

comm = MPI.COMM_WORLD
print("Hello world, I am rank $(MPI.Comm_rank(comm)) of $(MPI.Comm_size(comm))\n")
MPI.Barrier(comm)
40 changes: 40 additions & 0 deletions docs/examples/02-broadcast.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import MPI

MPI.Init()

comm = MPI.COMM_WORLD
N = 5
root = 0

if MPI.Comm_rank(comm) == root
println(" Running on $(MPI.Comm_size(comm)) processes")
end
MPI.Barrier(comm)

if MPI.Comm_rank(comm) == root
A = [i*(1.0 + im*2.0) for i = 1:N]
else
A = Array{ComplexF64}(undef, N)
end

MPI.Bcast!(A, root, comm)

println("rank = $(MPI.Comm_rank(comm)), A = $A")

if MPI.Comm_rank(comm) == root
B = Dict("foo" => "bar")
else
B = nothing
end

B = MPI.bcast(B, root, comm)
println("rank = $(MPI.Comm_rank(comm)), B = $B")

if MPI.Comm_rank(comm) == root
f = x -> x^2 + 2x - 1
else
f = nothing
end

f = MPI.bcast(f, root, comm)
println("rank = $(MPI.Comm_rank(comm)), f(3) = $(f(3))")
15 changes: 15 additions & 0 deletions docs/examples/03-reduce.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using MPI

MPI.Init()

comm = MPI.COMM_WORLD
root = 0

r = MPI.Comm_rank(comm)

sr = MPI.Reduce(r, +, root, comm)

if MPI.Comm_rank(comm) == root
println("sum of ranks = $sr")
end

28 changes: 28 additions & 0 deletions docs/examples/04-sendrecv.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using MPI

MPI.Init()

comm = MPI.COMM_WORLD
rank = MPI.Comm_rank(comm)
size = MPI.Comm_size(comm)

dst = mod(rank+1, size)
src = mod(rank-1, size)

N = 4

send_mesg = Array{Float64}(undef, N)
recv_mesg = Array{Float64}(undef, N)

fill!(send_mesg, Float64(rank))

rreq = MPI.Irecv!(recv_mesg, src, src+32, comm)

println("$rank: Sending $rank -> $dst = $send_mesg")
sreq = MPI.Isend(send_mesg, dst, rank+32, comm)

stats = MPI.Waitall!([rreq, sreq])

println("$rank: Received $src -> $rank = $recv_mesg")

MPI.Barrier(comm)
33 changes: 33 additions & 0 deletions docs/make.jl
Original file line number Diff line number Diff line change
@@ -1,6 +1,38 @@
using Documenter
using MPI

# generate example markdown
EXAMPLES = [
"Hello world" => "examples/01-hello.md",
"Broadcast" => "examples/02-broadcast.md",
"Reduce" => "examples/03-reduce.md",
"Send/receive" => "examples/04-sendrecv.md",
]

examples_md_dir = joinpath(@__DIR__,"src/examples")
isdir(examples_md_dir) || mkdir(examples_md_dir)

for (example_title, example_md) in EXAMPLES
example_jl = example_md[1:end-2]*"jl"
open(joinpath(@__DIR__, "src", example_md), "w") do mdfile
println(mdfile, "# $example_title")
println(mdfile)
println(mdfile, "`$example_jl`")
println(mdfile, "```julia")
write(mdfile, read(joinpath(@__DIR__,example_jl)))
println(mdfile, "```")
println(mdfile)

println(mdfile, "```")
println(mdfile, "> mpiexec -n 3 julia $example_jl")
cd(@__DIR__) do
write(mdfile, read(`$(MPI.mpiexec) -n 3 $(joinpath(Sys.BINDIR, Base.julia_exename())) --project $example_jl`))
end
println(mdfile, "```")
end
end


makedocs(
sitename = "MPI.jl",
format = Documenter.HTML(
Expand All @@ -11,6 +43,7 @@ makedocs(
"index.md",
"installing.md",
"usage.md",
"Examples" => EXAMPLES,
"functions.md",
]
)
Expand Down
113 changes: 12 additions & 101 deletions docs/src/usage.md
Original file line number Diff line number Diff line change
@@ -1,115 +1,26 @@
# Usage

## MPI-only mode
MPI is based on a [single program, multiple data (SPMD)](https://en.wikipedia.org/wiki/SPMD) model, where multiple processes are launched running independent programs, which then communicate as necessary via messages.

To run a Julia script with MPI, first make sure that `using MPI` or
`import MPI` is included at the top of your script. You should then be
able to run the MPI job as expected, e.g. to run [`examples/01-hello.jl`](https://github.com/JuliaParallel/MPI.jl/blob/master/examples/01-hello.jl),

```
mpirun -np 3 julia 01-hello.jl
```

## MPI and Julia parallel constructs together

In order for MPI calls to be made from a Julia cluster, it requires the use of
`MPIManager`, a cluster manager that will start the julia workers using `mpirun`

It has three modes of operation

- Only worker processes execute MPI code. The Julia master process executes outside of and
is not part of the MPI cluster. Free bi-directional TCP/IP connectivity is required
between all processes

- All processes (including Julia master) are part of both the MPI as well as Julia cluster.
Free bi-directional TCP/IP connectivity is required between all processes.

- All processes are part of both the MPI as well as Julia cluster. MPI is used as the transport
for julia messages. This is useful on environments which do not allow TCP/IP connectivity
between worker processes

### MPIManager: only workers execute MPI code

An example is provided in `examples/05-juliacman.jl`.
The julia master process is NOT part of the MPI cluster. The main script should be
launched directly, `MPIManager` internally calls `mpirun` to launch julia/MPI workers.
All the workers started via `MPIManager` will be part of the MPI cluster.

```
MPIManager(;np=Sys.CPU_THREADS, mpi_cmd=false, launch_timeout=60.0)
```

If not specified, `mpi_cmd` defaults to `mpirun -np $np`
`stdout` from the launched workers is redirected back to the julia session calling `addprocs` via a TCP connection.
Thus the workers must be able to freely connect via TCP to the host session.
The following lines will be typically required on the julia master process to support both julia and MPI:
A script should include `using MPI` and [`MPI.Init()`](@ref) statements, for example

```julia
# to import MPIManager
# examples/01-hello.jl
using MPI
MPI.Init()

# need to also import Distributed to use addprocs()
using Distributed

# specify, number of mpi workers, launch cmd, etc.
manager=MPIManager(np=4)

# start mpi workers and add them as julia workers too.
addprocs(manager)
```

To execute code with MPI calls on all workers, use `@mpi_do`.

`@mpi_do manager expr` executes `expr` on all processes that are part of `manager`.

For example:
```
@mpi_do manager begin
comm=MPI.COMM_WORLD
println("Hello world, I am $(MPI.Comm_rank(comm)) of $(MPI.Comm_size(comm))"))
end
```
executes on all MPI workers belonging to `manager` only

[`examples/05-juliacman.jl`](https://github.com/JuliaParallel/MPI.jl/blob/master/examples/05-juliacman.jl) is a simple example of calling MPI functions on all workers interspersed with Julia parallel methods.

This should be run _without_ `mpirun`:
```
julia 05-juliacman.jl
```

A single instation of `MPIManager` can be used only once to launch MPI workers (via `addprocs`).
To create multiple sets of MPI clusters, use separate, distinct `MPIManager` objects.

`procs(manager::MPIManager)` returns a list of julia pids belonging to `manager`
`mpiprocs(manager::MPIManager)` returns a list of MPI ranks belonging to `manager`

Fields `j2mpi` and `mpi2j` of `MPIManager` are associative collections mapping julia pids to MPI ranks and vice-versa.

### MPIManager: TCP/IP transport - all processes execute MPI code

Useful on environments which do not allow TCP connections outside of the cluster

An example is in [`examples/06-cman-transport.jl`](https://github.com/JuliaParallel/MPI.jl/blob/master/examples/06-cman-transport.jl):
```
mpirun -np 5 julia 06-cman-transport.jl TCP
comm = MPI.COMM_WORLD
println("Hello world, I am $(MPI.Comm_rank(comm)) of $(MPI.Comm_size(comm))")
MPI.Barrier(comm)
```

This launches a total of 5 processes, mpi rank 0 is the julia pid 1. mpi rank 1 is julia pid 2 and so on.

The program must call `MPI.start(TCP_TRANSPORT_ALL)` with argument `TCP_TRANSPORT_ALL`.
On mpi rank 0, it returns a `manager` which can be used with `@mpi_do`
On other processes (i.e., the workers) the function does not return


### MPIManager: MPI transport - all processes execute MPI code

`MPI.start` must be called with option `MPI_TRANSPORT_ALL` to use MPI as transport.
The program can then be launched via an MPI launch command (typically `mpiexec`, `mpirun` or `srun`), e.g.
```
mpirun -np 5 julia 06-cman-transport.jl MPI
$ mpiexec -n 3 julia --project examples/01-hello.jl
Hello world, I am rank 0 of 3
Hello world, I am rank 2 of 3
Hello world, I am rank 1 of 3
```
will run the example using MPI as transport.


## Finalizers

Expand Down
5 changes: 0 additions & 5 deletions examples/01-hello-impl.jl

This file was deleted.

12 changes: 0 additions & 12 deletions examples/01-hello.jl

This file was deleted.

45 changes: 0 additions & 45 deletions examples/02-broadcast-impl.jl

This file was deleted.

12 changes: 0 additions & 12 deletions examples/02-broadcast.jl

This file was deleted.

16 changes: 0 additions & 16 deletions examples/03-reduce-impl.jl

This file was deleted.

12 changes: 0 additions & 12 deletions examples/03-reduce.jl

This file was deleted.

Loading

0 comments on commit 5eb7607

Please sign in to comment.