1
- # pytest- mpi
1
+ # mpi-pytest
2
2
3
3
Pytest plugin that lets you run tests in parallel with MPI.
4
4
5
- ## Installation
5
+ ` mpi-pytest ` provides:
6
6
7
- To install ` pytest-mpi ` simply run:
7
+ * A ` parallel ` marker indicating that the test must be run under MPI.
8
+ * A ` parallel_assert ` function for evaluating assertions in a deadlock-safe way.
8
9
9
- ```
10
- $ python -m pip install /path/to/pytest-mpi-repo
11
- ```
12
-
13
- ## Usage
10
+ ## ` parallel ` marker
14
11
15
12
Writing a parallel test simply requires marking the test with the ` parallel ` marker:
16
13
@@ -49,35 +46,33 @@ and renamed to, in this case, `test_my_code_on_variable_nprocs[nprocs=1]`,
49
46
` test_my_code_on_variable_nprocs[nprocs=2] ` and
50
47
` test_my_code_on_variable_nprocs[nprocs=3] ` .
51
48
52
- ### Extra markers
53
-
54
- When running the code with these ` parallel ` markers, ` pytest-mpi ` adds extra markers
49
+ When running the code with these ` parallel ` markers, ` mpi-pytest ` adds extra markers
55
50
to each test to allow one to select all tests with a particular number of processors.
56
- For example, to select all parallel tests on 3 processors, one should run
51
+ For example, to select all parallel tests on 3 processors, one should run:
57
52
58
53
``` bash
59
- $ pytest -m " parallel[3]"
54
+ $ mpiexec -n 3 pytest -m parallel[3]
60
55
```
61
56
62
- For serial tests - those either unmarked or marked ` @pytest.mark.parallel(1) ` - one
63
- can select these by running
57
+ Serial tests - those either unmarked or marked ` @pytest.mark.parallel(1) ` - can
58
+ be selected by running:
64
59
65
60
``` bash
66
61
$ pytest -m " not parallel or parallel[1]"
67
62
```
68
63
69
64
### Forking mode
70
65
71
- ` pytest- mpi` can be run in one of two modes: forking or non-forking. The former
66
+ ` mpi-pytest ` can be used in one of two modes: forking or non-forking. The former
72
67
works as follows:
73
68
74
- 1 . The user calls ` pytest ` (not ` mpiexec -n <# proc> pytest ` ! ). This launches
69
+ 1 . The user calls ` pytest ` (not ` mpiexec -n <# proc> pytest ` ). This launches
75
70
the "parent" ` pytest ` process.
76
71
2 . This parent ` pytest ` process collects all the tests and begins to run them.
77
72
3 . When a test is found with the ` parallel ` marker, rather than executing the
78
73
function as before, a subprocess is forked calling
79
- ` mpiexec -np <# proc> pytest this_specific_test_file.py::this_specific_test ` .
80
- This produces ` <# proc> ` " child" ` pytest ` processes that execute the
74
+ ` mpiexec -n <# proc> pytest this_specific_test_file.py::this_specific_test ` .
75
+ This produces ` <# proc> ` ' child' ` pytest ` processes that execute the
81
76
test together.
82
77
4 . If this terminates successfully then the test is considered to have passed.
83
78
@@ -87,35 +82,59 @@ This is convenient for development for a number of reasons:
87
82
* It is not necessary to wrap ` pytest ` invocations with ` mpiexec ` calls, and
88
83
all parallel and serial tests can be run at once.
89
84
90
- However, the forking mode of ` pytest-mpi ` is restricted in that only one mainstream
91
- MPI distribution ([ MPICH] ( https://www.mpich.org/ ) ) supports nested calls to
92
- ` MPI_Init ` . If your "parent" ` pytest ` process initialises MPI (for instance by
93
- executing ` from petsc4py import PETSc ` ) then this will cause non-MPICH MPI
94
- distributions to crash. Further, forking a subprocess can be expensive since a
95
- completely fresh Python interpreter is launched each time.
85
+ There are however a number of downsides:
86
+
87
+ * Only one mainstream MPI distribution ([ MPICH] ( https://www.mpich.org/ ) ) supports
88
+ nested calls to ` MPI_Init ` . If your 'parent' ` pytest ` process initialises MPI
89
+ (for instance by executing ` from mpi4py import MPI ` ) then this will cause non-MPICH
90
+ MPI distributions to crash.
91
+ * Forking a subprocess can be expensive since a completely fresh Python interpreter
92
+ is launched each time.
93
+ * Sandboxing each test means that polluted global state at the end of a test cannot
94
+ be detected.
96
95
97
96
### Non-forking mode
98
97
99
- With these significant limitations in mind, ` pytest- mpi` therefore also supports
98
+ With these significant limitations in mind, ` mpi-pytest ` therefore also supports
100
99
a non-forking mode. To use it, one simply needs to wrap the ` pytest ` invocation
101
100
with ` mpiexec ` , no additional configuration is necessary. For example, to run
102
101
all of the parallel tests on 2 ranks one needs to execute:
103
102
104
103
``` bash
105
- $ mpiexec -n 2 pytest -m " parallel[2]"
104
+ $ mpiexec -n 2 pytest -m parallel[2]
106
105
```
107
106
108
- This approach is agnostic to MPI distribution, and free from the forking startup
109
- overhead, but has a number of disadvantages:
107
+ ## ` parallel_assert `
108
+
109
+ Using regular ` assert ` statements can be unsafe in parallel codes. Consider the
110
+ code:
111
+
112
+ ``` py
113
+ @pytest.mark.parallel (2 )
114
+ def test_something ():
115
+ # this will only fail on *some* ranks
116
+ assert COMM_WORLD .rank == 0
110
117
111
- * ` pytest-xdist ` is strictly disallowed as threading would lead to deadlocks. It
112
- is therefore impossible to take full advantage of machines with many cores.
113
- * A different ` mpiexec ` instruction is required for each level of parallelism.
114
- Attempting to run with ` mpiexec ` with a mismatching number of processes to the
115
- parallel marker will result in an error.
118
+ # this will hang
119
+ COMM_WORLD .barrier()
120
+ ```
121
+
122
+ One can see that failing assertions on some ranks but not others will violate SPMD
123
+ and lead to deadlocks. To avoid this, ` mpi-pytest ` provides a ` parallel_assert `
124
+ function used as follows:
125
+
126
+ ``` py
127
+ from pytest_mpi import parallel_assert
128
+
129
+ @pytest.mark.parallel (2 )
130
+ def test_something ():
131
+ # this will fail on *all* ranks
132
+ parallel_assert(lambda : COMM_WORLD .rank == 0 )
133
+ ...
134
+ ```
116
135
117
136
## Configuration
118
137
119
- ` pytest- mpi` respects the environment variable ` PYTEST_MPI_MAX_NPROCS ` , which defines
138
+ ` mpi-pytest ` respects the environment variable ` PYTEST_MPI_MAX_NPROCS ` , which defines
120
139
the maximum number of processes that can be requested by a parallel marker. If this
121
140
value is exceeded an error will be raised.
0 commit comments