source: CIVL/docs/challenge.md@ 6b26bbb

1.23 2.0 main
Last change on this file since 6b26bbb was 135e8cf, checked in by Youngjun Lee <youngjunlee7@…>, 3 weeks ago

Initial Markdown documents

  • Property mode set to 100644
File size: 8.3 KB
RevLine 
[135e8cf]1# A Challenge Exercise for Twente
2
3## A Dissemination Barrier
4
5Designing efficient barriers is a big problem in concurrency theory.
6A barrier is a synchronization operation that is invoked by all threads or processes
7in a concurrent system. The defining property is that no process can leave
8the barrier until every process has entered the barrier. Of course, a barrier
9should not deadlock --- once all processes have entered, they should all
10be able to leave. A barrier should also be re-useable, i.e., it can be invoked
11more than once.
12
13The dissemination barrier works as follows. There are n processes, numbered
14`0 .. n-1`. The order is treated as cyclical. The protocol goes through
15`ceil(log2(n))` stages, `i = 0, 1, ...` At stage $i$, each process sends a message to the process
16$2^{i}$ to its "right", and waits to receive a message from the process $2^{i}$ to its "left".
17The content of the message is irrelevant (and can be empty).
18
19**Challenge 1**: Design a dissemination barrier using MPI. Verify it up to some
20bound on the number of processes using CIVL.
21
22**Challenge 2**: Design a dissemination barrier using semaphores. Verify it up
23to some bound on the number of threads using CIVL.
24
25In both cases, there should be a function named `barrier` (possibly with
26parameters) that all processes/threads call in order to participate in a barrier.
27The function could use global variables.
28
29In both cases, part of the challenge is to come up with an appropriate driver
30that tests the barrier. Try to develop the most universal (challenging) driver
31you can. Can you make one that guarantees the barrier is correct?
32
33## CIVL-C Reference
34
35For installation instructions: [CIVL Manual: Introduction](introduction.md).
36
37Almost everything from standard sequential C is allowed.
38The standard library is partially implemented, including:
39
40`printf`, `assert`, `malloc`, `free`, ...
41
42Be sure to include the appropriate headers.
43
44In standard C, function definitions can occur only in file scope.
45In CIVL-C, they can occur in any scope.
46
47* `$input decl`
48
49 Declares a variable in file scope to be an input variable. An input variable
50 `X` is initialized according to the following protocol: (1) if a value for `X`
51 is specified on the command line via `-inputX=val`, then `val` is the
52 initial value; (2) otherwise, if an initializer is present in the declaration, the
53 initializer is used; (3) otherwise, `X` is assigned an unconstrained value
54 of its type---when using symbolic execution, it is assigned a fresh symbolic
55 constant.
56
57* `$when (expr) stmt`
58
59 a guarded command. Blocks until `expr`
60 is true. Executes the first atomic action of `stmt` atomically
61 with the evaluation of `expr` to true. Example: `$when (s>0) s--`
62 defines Dijkstra's P operation on semaphores. The V operation
63 is just `s++`. Note that `s--` and `s++` are both atomic in CIVL-C.
64
65* `_Bool`
66
67 The Boolean type. Values: 0 and 1. This is the same as C11.
68
69* `$proc`
70
71 the process type. A value of this type refers to a process.
72
73* `$spawn stmt`
74
75 creates a new process executing the statement. Returns a value of type `$proc`
76 referencing the new process.
77
78* `$wait($proc p)`
79
80 blocks until process p terminates
81
82* `$parfor (int i:a..b) stmt`
83
84 spawns threads, one for each i in the range a..b, each executing `stmt`.
85 Waits for them to terminate.
86
87* `$assert(expr)`
88
89 An assertion. This is treated exactly the same as C's `assert`, except with
90 the latter you must include `assert.h`. CIVL-C has a richer assertion
91 language that C---e.g., you can use quantifiers.
92
93* `$assume(expr)`
94
95 Assumes this expression is true, i.e., if the expression is not true, the
96 the execution is not considered to be a valid execution of the program.
97
98* `$forall` and `$exists`
99
100 Mean what you think. Here are some examples to show the syntax:
101
102 * `$forall (double x | x>1) x>0` -- for all doubles `x` that are greater than 1, `x` is greater than 0
103 * `$forall (int i | a<=i && i<=b) p(i)` -- for all i between a and b (inclusive), `p(i)` holds
104 * `$forall (int i:a..b) p(i)` -- same as above, but more convenient
105
106* `int $choose_int(int n)`
107
108 Returns an integer nondeterministically chosen from `0 .. n-1`.
109
110* `$choose { stmt1 stmt2 ... }`
111
112 Structural nondeterminism. Chooses one of the enabled statements
113 nondeterministically. The statements are often guarded using `$when`.
114
115## MPI Reference
116
117### Constants
118
119* `MPI_COMM_WORLD` (type `MPI_Comm`)
120* `MPI_STATUS_IGNORE` (type `MPI_Status*`)
121* `MPI_ANY_SOURCE` (type `int`)
122* `MPI_ANY_TAG` (type `int`)
123* `MPI_INT` (type `MPI_Datatype`)
124* `MPI_DOUBLE` (type `MPI_Datatype`)
125
126### Functions
127
128* `MPI_Init(NULL, NULL)`
129* `MPI_Finalize()`
130
131`MPI_Init` must be called before other MPI functions. `MPI_Finalize`
132must be called before termination and after all other MPI functions.
133
134* `MPI_Comm_size(MPI_Comm comm, int *nprocs)`
135* `MPI_Comm_rank(MPI_Comm comm, int *rank)`
136
137Get the number of processes in the communicator, and the "rank" (ID
138number) of this process within the communicator.
139
140* `int MPI_Send(const void *buf, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm)`
141
142Sends a message to the process of rank dest.
143You can use `NULL` for `buf` if `count` is 0 --- an empty message.
144Note that `MPI_Send` may be forced to synchronize --- i.e., it can block
145until the receiving process reaches a matching receive statement.
146
147* `int MPI_Recv(void *buf, int count, MPI_Datatype datatype, int source, int tag, MPI_Comm comm, MPI_Status *status)`
148
149Receives a message from the specified source process.
150`source` can be `MPI_ANY_SOURCE` --- to receive from any source ("wildcard").
151status can be `MPI_STATUS_IGNORE`, if you don't care about the status.
152Otherwise, declare a variable of type `MPI_Status` and pass a pointer to it.
153
154```c
155int MPI_Sendrecv(const void *sendbuf, int sendcount, MPI_Datatype sendtype,
156 int dest, int sendtag,
157 void *recvbuf, int recvcount, MPI_Datatype recvtype,
158 int source, int recvtag,
159 MPI_Comm comm, MPI_Status *status);
160```
161
162Combines one `MPI_Send` operation and one `MPI_Recv` operation into a
163single command. It behaves as if the two operations execute
164concurrently in separate threads. Used to avoid deadlocks that could
165result if all processes do `MPI_Send; MPI_Recv`.
166
167
168## A two-thread barrier using semaphores in CIVL-C
169
170```civl
171#include <stdio.h>
172
173int s1=0, s2=0;
174#define sem_wait(s) $when (s>0) s--;
175#define sem_post(s) s++;
176const int N=10;
177int i1=0, i2=0;
178
179void f1() {
180 while (i1<N) {
181 printf("thread 1 at iteration %d\n", i1);
182 fflush(stdout);
183 i1++;
184 sem_post(s1);
185 sem_wait(s2);
186 $assert(i1==i2);
187 sem_post(s1);
188 sem_wait(s2);
189 }
190}
191
192void f2() {
193 while (i2<N) {
194 printf("thread 2 at iteration %d\n", i2);
195 fflush(stdout);
196 i2++;
197 sem_post(s2);
198 sem_wait(s1);
199 $assert(i1==i2);
200 sem_post(s2);
201 sem_wait(s1);
202 }
203}
204
205int main() {
206 $proc t1 = $spawn f1(), t2 = $spawn f2();
207 $wait(t1);
208 $wait(t2);
209}
210```
211
212## An example MPI program
213
214```c
215#include <mpi.h>
216#include <stdio.h>
217#include <assert.h>
218
219#define FROMRIGHT 0
220#define FROMLEFT 1
221
222int main() {
223 int rank, size;
224 int recv;
225 int left, right;
226
227 MPI_Init(NULL, NULL);
228 MPI_Comm_rank(MPI_COMM_WORLD, &rank);
229 MPI_Comm_size(MPI_COMM_WORLD, &size);
230 left = (rank == 0) ? size - 1 : rank - 1;
231 right = (rank == (size - 1)) ? 0 : rank + 1;
232 MPI_Sendrecv(&rank, 1, MPI_INT, left, FROMRIGHT, &recv, 1, MPI_INT,
233 right, FROMRIGHT, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
234 assert(recv == right);
235 MPI_Sendrecv(&rank, 1, MPI_INT, right, FROMLEFT, &recv, 1,
236 MPI_INT, left, FROMLEFT, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
237 assert(recv == left);
238 MPI_Finalize();
239 return 0;
240}
241```
242
243
244## Verifying C/MPI Programs
245
246CIVL can verify C/MPI programs that use a subset of MPI. The instructions for sequential programs apply equally to MPI programs. In addition, one must specify either (1) the number of processes for the MPI program, or (2) an upper and a lower bound on the number of processes for the MPI program.
247
248In the following example, the C/MPI program `ring.c` is verified for exactly 5 processes:
249
250```sh
251civl verify -input_mpi_nprocs=5 ring.c
252```
253
254In the following example, `ring.c` is verified for any number of processes between 2 and 5, inclusive:
255
256```sh
257civl verify -input_mpi_nprocs_lo=2 -input_mpi_nprocs_hi=5 ring.c
258```
259
260
Note: See TracBrowser for help on using the repository browser.