1 /* Copyright (c) 2017-2023. The SimGrid Team. All rights reserved. */
3 /* This program is free software; you can redistribute it and/or modify it
4 * under the terms of the license (GNU LGPL) which comes with this package. */
6 #include "src/3rd-party/catch.hpp"
7 #include "src/mc/explo/udpor/Configuration.hpp"
8 #include "src/mc/explo/udpor/ExtensionSetCalculator.hpp"
9 #include "src/mc/explo/udpor/History.hpp"
10 #include "src/mc/explo/udpor/Unfolding.hpp"
12 using namespace simgrid::mc;
13 using namespace simgrid::mc::udpor;
15 TEST_CASE("simgrid::mc::udpor: Testing Computation with AsyncSend/AsyncReceive Only")
17 // This test checks that the unfolding constructed for the very
18 // simple program described below is extended correctly. Each
19 // step of UDPOR is observed to ensure that computations are carried
20 // out correctly. The program described is simply:
23 // AsyncSend(m) AsyncRecv(m)
25 // The unfolding of the simple program is as follows:
29 // (a) 1: AsyncSend(m) (b) 2: AsyncRecv(m)
31 const int times_considered = 0;
33 const unsigned mbox = 0;
34 const uintptr_t comm = 0;
38 SECTION("Computing ex({⊥}) with 1: AsyncSend")
40 // Consider the extension with `1: AsyncSend(m)`
43 const uintptr_t sbuff = 0;
44 const size_t size = 100;
46 const auto async_send =
47 std::make_shared<CommSendTransition>(issuer, times_considered, comm, mbox, sbuff, size, tag);
48 const auto incremental_exC = ExtensionSetCalculator::partially_extend(C, &U, async_send);
50 // Check that the events have been added to `U`
51 REQUIRE(U.size() == 1);
53 // Make assertions about the contents of ex(C)
54 UnfoldingEvent e(EventSet(), async_send);
55 REQUIRE(incremental_exC.contains_equivalent_to(&e));
58 SECTION("Computing ex({⊥}) with 2: AsyncRecv")
60 // Consider the extension with `2: AsyncRecv(m)`
63 const uintptr_t rbuff = 0;
65 const auto async_recv = std::make_shared<CommRecvTransition>(issuer, times_considered, comm, mbox, rbuff, tag);
66 const auto incremental_exC = ExtensionSetCalculator::partially_extend(C, &U, async_recv);
68 // Check that the events have been added to `U`
69 REQUIRE(U.size() == 1);
71 // Make assertions about the contents of ex(C)
72 UnfoldingEvent e(EventSet(), async_recv);
73 REQUIRE(incremental_exC.contains_equivalent_to(&e));
76 SECTION("Computing ex({⊥}) fully")
78 // Consider the extension with `1: AsyncSend(m)`
80 const uintptr_t rbuff = 0;
81 const uintptr_t sbuff = 0;
82 const size_t size = 100;
84 const auto async_send = std::make_shared<CommSendTransition>(1, times_considered, comm, mbox, sbuff, size, tag);
85 const auto incremental_exC_send = ExtensionSetCalculator::partially_extend(C, &U, async_send);
87 // Check that the events have been added to `U`
88 REQUIRE(U.size() == 1);
90 // Make assertions about the contents of ex(C)
91 UnfoldingEvent e_send(EventSet(), async_send);
92 REQUIRE(incremental_exC_send.contains_equivalent_to(&e_send));
94 // Consider the extension with `2: AsyncRecv(m)`
95 const auto async_recv = std::make_shared<CommRecvTransition>(2, times_considered, comm, mbox, rbuff, tag);
96 const auto incremental_exC_recv = ExtensionSetCalculator::partially_extend(C, &U, async_recv);
98 // Check that the events have been added to `U`
99 REQUIRE(U.size() == 2);
101 // Make assertions about the contents of ex(C)
102 UnfoldingEvent e_recv(EventSet(), async_recv);
103 REQUIRE(incremental_exC_recv.contains_equivalent_to(&e_recv));
106 SECTION("Computing the full sequence of extensions")
109 const uintptr_t rbuff = 0;
110 const uintptr_t sbuff = 0;
111 const size_t size = 100;
113 // Consider the extension with `1: AsyncSend(m)`
114 const auto async_send = std::make_shared<CommSendTransition>(1, times_considered, comm, mbox, sbuff, size, tag);
115 const auto incremental_exC_send = ExtensionSetCalculator::partially_extend(C, &U, async_send);
117 // Check that event `a` has been added to `U`
118 REQUIRE(U.size() == 1);
119 UnfoldingEvent e_send(EventSet(), async_send);
120 REQUIRE(incremental_exC_send.contains_equivalent_to(&e_send));
122 // Consider the extension with `2: AsyncRecv(m)`
123 const auto async_recv = std::make_shared<CommRecvTransition>(2, times_considered, comm, mbox, rbuff, tag);
124 const auto incremental_exC_recv = ExtensionSetCalculator::partially_extend(C, &U, async_recv);
126 // Check that event `b` has been added to `U`
127 REQUIRE(U.size() == 2);
128 UnfoldingEvent e_recv(EventSet(), async_recv);
129 REQUIRE(incremental_exC_recv.contains_equivalent_to(&e_recv));
131 // At this point, UDPOR will pick one of the two events equivalent to
132 // `e_recv` and e`_send`
134 // Suppose it picks the event labeled `a` in the graph above first
135 // (ultimately, UDPOR will choose to search both directions since
136 // {⊥, b} will be an alternative to {⊥, a})
138 const auto* e_a = *incremental_exC_send.begin();
139 const auto* e_b = *incremental_exC_recv.begin();
141 SECTION("Pick `a` first (ex({⊥, a}))")
143 // After picking `a`, we need only consider the extensions
144 // possible with `2: AsyncRecv(m)` since actor 1 no longer
145 // has any actions to run
146 Configuration C_with_send{e_a};
147 const auto incremental_exC_with_send = ExtensionSetCalculator::partially_extend(C_with_send, &U, async_recv);
149 // Check that event `b` has not been duplicated
150 REQUIRE(U.size() == 2);
152 // Indeed, in this case we assert that the SAME identity has been
153 // supplied by the unfolding (it should note that `ex({⊥, a})`
154 // and `ex({⊥})` have an overlapping event `b`)
155 REQUIRE(incremental_exC_with_send.contains(e_b));
158 SECTION("Pick `b` first (ex({⊥, b}))")
160 // After picking `b`, we need only consider the extensions
161 // possible with `1: AsyncSend(m)` since actor 2 no longer
162 // has any actions to run
163 Configuration C_with_recv{e_b};
164 const auto incremental_exC_with_recv = ExtensionSetCalculator::partially_extend(C_with_recv, &U, async_send);
166 // Check that event `a` has not been duplicated
167 REQUIRE(U.size() == 2);
169 // Indeed, in this case we assert that the SAME identity has been
170 // supplied by the unfolding (it should note that `ex({⊥, b})`
171 // and `ex({⊥})` have an overlapping event `a`)
172 REQUIRE(incremental_exC_with_recv.contains(e_a));
177 TEST_CASE("simgrid::mc::udpor: Testing Waits, Receives, and Sends")
179 // We're going to follow UDPOR down one path of computation
180 // in a relatively simple program (although the unfolding quickly
181 // becomes quite complex)
184 // AsyncSend(m) AsyncRecv(m)
187 // The unfolding of the simple program is as follows:
190 // (a) 1: AsyncSend(m) (b) 2: AsyncRecv(m)
195 // (c) 1: Wait(m) (d) 2: Wait(m)
196 const int times_considered = 0;
198 const unsigned mbox = 0;
199 const uintptr_t comm = 0x800;
200 const uintptr_t rbuff = 0x200;
201 const uintptr_t sbuff = 0x400;
202 const size_t size = 100;
203 const bool timeout = false;
206 const auto comm_send = std::make_shared<CommSendTransition>(1, times_considered, comm, mbox, sbuff, size, tag);
207 const auto comm_recv = std::make_shared<CommRecvTransition>(2, times_considered, comm, mbox, rbuff, tag);
208 const auto comm_wait_1 =
209 std::make_shared<CommWaitTransition>(1, times_considered, timeout, comm, 1, 2, mbox, sbuff, rbuff, size);
210 const auto comm_wait_2 =
211 std::make_shared<CommWaitTransition>(2, times_considered, timeout, comm, 1, 2, mbox, sbuff, rbuff, size);
213 // 1. UDPOR will attempt to expand first ex({⊥})
216 const auto incremental_exC_send = ExtensionSetCalculator::partially_extend(Configuration(), &U, comm_send);
217 { // Assert that event `a` has been added
218 UnfoldingEvent e_send(EventSet(), comm_send);
219 REQUIRE(incremental_exC_send.size() == 1);
220 REQUIRE(incremental_exC_send.contains_equivalent_to(&e_send));
221 REQUIRE(U.size() == 1);
224 const auto incremental_exC_recv = ExtensionSetCalculator::partially_extend(Configuration(), &U, comm_recv);
225 { // Assert that event `b` has been added
226 UnfoldingEvent e_recv(EventSet(), comm_recv);
227 REQUIRE(incremental_exC_recv.size() == 1);
228 REQUIRE(incremental_exC_recv.contains_equivalent_to(&e_recv));
229 REQUIRE(U.size() == 2);
233 // 2. UDPOR will then attempt to expand ex({⊥, a}) or ex({⊥, b}). Both have
234 // parallel effects and should simply return events already added to ex(C)
236 // NOTE: Note that only once actor is enabled in both cases, meaning that
237 // we need only consider one incremental expansion for each
239 const auto* e_a = *incremental_exC_send.begin();
240 const auto* e_b = *incremental_exC_recv.begin();
242 // --- ex({⊥, a}) ---
243 const auto incremental_exC_recv2 = ExtensionSetCalculator::partially_extend(Configuration({e_a}), &U, comm_recv);
244 { // Assert that no event has been added and that
245 // e_b is contained in the extension set
246 UnfoldingEvent e_send(EventSet(), comm_send);
247 REQUIRE(incremental_exC_recv2.size() == 1);
248 REQUIRE(incremental_exC_recv2.contains(e_b));
250 // Here, `e_a` shouldn't be added again
251 REQUIRE(U.size() == 2);
253 // --- ex({⊥, a}) ---
255 // --- ex({⊥, b}) ---
256 const auto incremental_exC_send2 = ExtensionSetCalculator::partially_extend(Configuration({e_b}), &U, comm_send);
257 { // Assert that no event has been added and that
258 // e_a is contained in the extension set
259 REQUIRE(incremental_exC_send2.size() == 1);
260 REQUIRE(incremental_exC_send2.contains(e_a));
262 // Here, `e_b` shouldn't be added again
263 REQUIRE(U.size() == 2);
265 // --- ex({⊥, b}) ---
267 // 3. Expanding from ex({⊥, a, b}) brings in both `CommWait` events since they
268 // become enabled as soon as the communication has been paired
270 // --- ex({⊥, a, b}) ---
271 const auto incremental_exC_wait_actor_1 =
272 ExtensionSetCalculator::partially_extend(Configuration({e_a, e_b}), &U, comm_wait_1);
273 { // Assert that events `c` has been added
274 UnfoldingEvent e_wait_1(EventSet({e_a, e_b}), comm_wait_1);
275 REQUIRE(incremental_exC_wait_actor_1.size() == 1);
276 REQUIRE(incremental_exC_wait_actor_1.contains_equivalent_to(&e_wait_1));
277 REQUIRE(U.size() == 3);
280 const auto incremental_exC_wait_actor_2 =
281 ExtensionSetCalculator::partially_extend(Configuration({e_a, e_b}), &U, comm_wait_2);
282 { // Assert that events `d` has been added
283 UnfoldingEvent e_wait_2(EventSet({e_a, e_b}), comm_wait_2);
284 REQUIRE(incremental_exC_wait_actor_2.size() == 1);
285 REQUIRE(incremental_exC_wait_actor_2.contains_equivalent_to(&e_wait_2));
286 REQUIRE(U.size() == 4);
288 // --- ex({⊥, a, b}) ---
290 // 4. Expanding from either wait action should simply yield the other event
291 // with a wait action associated with it.
292 // This is analogous to the scenario before with send and receive
293 // ex({⊥, a, b, c}) or ex({⊥, a, b, d})
295 const auto* e_c = *incremental_exC_wait_actor_1.begin();
296 const auto* e_d = *incremental_exC_wait_actor_2.begin();
298 // --- ex({⊥, a, b, d}) ---
299 const auto incremental_exC_wait_actor_1_2 =
300 ExtensionSetCalculator::partially_extend(Configuration({e_a, e_b, e_d}), &U, comm_wait_1);
301 { // Assert that no event has been added and that
302 // `e_c` is contained in the extension set
303 REQUIRE(incremental_exC_wait_actor_1_2.size() == 1);
304 REQUIRE(incremental_exC_wait_actor_1_2.contains(e_c));
305 REQUIRE(U.size() == 4);
307 // --- ex({⊥, a, b, d}) ---
309 // --- ex({⊥, a, b, c}) ---
310 const auto incremental_exC_wait_actor_2_2 =
311 ExtensionSetCalculator::partially_extend(Configuration({e_a, e_b, e_c}), &U, comm_wait_2);
312 { // Assert that no event has been added and that
313 // `e_d` is contained in the extension set
314 REQUIRE(incremental_exC_wait_actor_2_2.size() == 1);
315 REQUIRE(incremental_exC_wait_actor_2_2.contains(e_d));
316 REQUIRE(U.size() == 4);
318 // --- ex({⊥, a, b, c}) ---