diff --git a/partitioned-communication/Makefile b/partitioned-communication/Makefile new file mode 100644 index 0000000..70b98af --- /dev/null +++ b/partitioned-communication/Makefile @@ -0,0 +1,367 @@ +BIN=./bin/ +OBJ=./obj/ + +# C compiler +CC=gcc + +# MPI +# if required, add paths to mpi.h and any other necessary headers (e.g., UCX) +CFLAGS= +# if required, add paths to ompi libraries and any other necessary libraries +LDFLAGS= + +.PHONY: clean + +all: test_cancel0.x test_datatype0.x test_datatype1.x test_datatype2.x test_datatype3.x test_datatype4.x test_datatype5.x test_example1a.x test_example1b.x test_example2.x test_example3a.x test_example3b.x test_example3c.x test_free0.x test_init0.x test_init1.x test_init2.x test_local0.x test_local1.x test_numparts0.x test_numparts1.x test_order0.x test_parrived0.x test_parrived1.x test_parrived2.x test_pready0.x test_pready1.x test_pready2.x test_pready3.x test_pready4.x test_pready_list0.x test_pready_list1.x test_pready_range0.x test_partitions0.x test_partitions1.x test_partitions2.x test_partitions3.x test_startall0.x test_state0.x test_wildcard0.x test_wildcard1.x test_zerocount0.x test_zerocount1.x + +test_cancel0.x: test_cancel0.o + mkdir -p ${BIN} + $(CC) -o ${BIN}$@ -L${OBJ} $(LDFLAGS) $(addprefix ${OBJ}, $^) + +test_cancel0.o: test_cancel0.c + mkdir -p ${OBJ} + $(CC) -c $< -o ${OBJ}$@ $(CFLAGS) + +test_datatype0.x: test_datatype0.o + mkdir -p ${BIN} + $(CC) -o ${BIN}$@ -L${OBJ} $(LDFLAGS) $(addprefix ${OBJ}, $^) + +test_datatype0.o: test_datatype0.c + mkdir -p ${OBJ} + $(CC) -c $< -o ${OBJ}$@ $(CFLAGS) + +test_datatype1.x: test_datatype1.o + mkdir -p ${BIN} + $(CC) -o ${BIN}$@ -L${OBJ} $(LDFLAGS) $(addprefix ${OBJ}, $^) + +test_datatype1.o: test_datatype1.c + mkdir -p ${OBJ} + $(CC) -c $< -o ${OBJ}$@ $(CFLAGS) + +test_datatype2.x: test_datatype2.o + mkdir -p ${BIN} + $(CC) -o ${BIN}$@ -L${OBJ} $(LDFLAGS) $(addprefix ${OBJ}, $^) + +test_datatype2.o: test_datatype2.c + mkdir -p ${OBJ} + $(CC) -c $< -o ${OBJ}$@ $(CFLAGS) + +test_datatype3.x: test_datatype3.o + mkdir -p ${BIN} + $(CC) -o ${BIN}$@ -L${OBJ} $(LDFLAGS) $(addprefix ${OBJ}, $^) + +test_datatype3.o: test_datatype3.c + mkdir -p ${OBJ} + $(CC) -c $< -o ${OBJ}$@ $(CFLAGS) + +test_datatype4.x: test_datatype4.o + mkdir -p ${BIN} + $(CC) -o ${BIN}$@ -L${OBJ} $(LDFLAGS) $(addprefix ${OBJ}, $^) + +test_datatype4.o: test_datatype4.c + mkdir -p ${OBJ} + $(CC) -c $< -o ${OBJ}$@ $(CFLAGS) + +test_datatype5.x: test_datatype5.o + mkdir -p ${BIN} + $(CC) -o ${BIN}$@ -L${OBJ} $(LDFLAGS) $(addprefix ${OBJ}, $^) + +test_datatype5.o: test_datatype5.c + mkdir -p ${OBJ} + $(CC) -c $< -o ${OBJ}$@ $(CFLAGS) + +test_example1a.x: test_example1a.o + mkdir -p ${BIN} + $(CC) -o ${BIN}$@ -L${OBJ} $(LDFLAGS) $(addprefix ${OBJ}, $^) + +test_example1a.o: test_example1a.c + mkdir -p ${OBJ} + $(CC) -c $< -o ${OBJ}$@ $(CFLAGS) + +test_example1b.x: test_example1b.o + mkdir -p ${BIN} + $(CC) -o ${BIN}$@ -L${OBJ} $(LDFLAGS) $(addprefix ${OBJ}, $^) + +test_example1b.o: test_example1b.c + mkdir -p ${OBJ} + $(CC) -c $< -o ${OBJ}$@ $(CFLAGS) + +test_example2.x: test_example2.o + mkdir -p ${BIN} + $(CC) -o ${BIN}$@ -L${OBJ} $(LDFLAGS) $(addprefix ${OBJ}, $^) -fopenmp + +test_example2.o: test_example2.c + mkdir -p ${OBJ} + $(CC) -c $< -o ${OBJ}$@ $(CFLAGS) -fopenmp + +test_example3a.x: test_example3a.o + mkdir -p ${BIN} + $(CC) -o ${BIN}$@ -L${OBJ} $(LDFLAGS) $(addprefix ${OBJ}, $^) -fopenmp + +test_example3a.o: test_example3a.c + mkdir -p ${OBJ} + $(CC) -c $< -o ${OBJ}$@ $(CFLAGS) -fopenmp + +test_example3b.x: test_example3b.o + mkdir -p ${BIN} + $(CC) -o ${BIN}$@ -L${OBJ} $(LDFLAGS) $(addprefix ${OBJ}, $^) -fopenmp + +test_example3b.o: test_example3b.c + mkdir -p ${OBJ} + $(CC) -c $< -o ${OBJ}$@ $(CFLAGS) -fopenmp + +test_example3c.x: test_example3c.o + mkdir -p ${BIN} + $(CC) -o ${BIN}$@ -L${OBJ} $(LDFLAGS) $(addprefix ${OBJ}, $^) -fopenmp + +test_example3c.o: test_example3c.c + mkdir -p ${OBJ} + $(CC) -c $< -o ${OBJ}$@ $(CFLAGS) -fopenmp + +test_free0.x: test_free0.o + mkdir -p ${BIN} + $(CC) -o ${BIN}$@ -L${OBJ} $(LDFLAGS) $(addprefix ${OBJ}, $^) + +test_free0.o: test_free0.c + mkdir -p ${OBJ} + $(CC) -c $< -o ${OBJ}$@ $(CFLAGS) + +test_init0.x: test_init0.o + mkdir -p ${BIN} + $(CC) -o ${BIN}$@ -L${OBJ} $(LDFLAGS) $(addprefix ${OBJ}, $^) + +test_init0.o: test_init0.c + mkdir -p ${OBJ} + $(CC) -c $< -o ${OBJ}$@ $(CFLAGS) + +test_init1.x: test_init1.o + mkdir -p ${BIN} + $(CC) -o ${BIN}$@ -L${OBJ} $(LDFLAGS) $(addprefix ${OBJ}, $^) + +test_init1.o: test_init1.c + mkdir -p ${OBJ} + $(CC) -c $< -o ${OBJ}$@ $(CFLAGS) + +test_init2.x: test_init2.o + mkdir -p ${BIN} + $(CC) -o ${BIN}$@ -L${OBJ} $(LDFLAGS) $(addprefix ${OBJ}, $^) + +test_init2.o: test_init2.c + mkdir -p ${OBJ} + $(CC) -c $< -o ${OBJ}$@ $(CFLAGS) + +test_local0.x: test_local0.o + mkdir -p ${BIN} + $(CC) -o ${BIN}$@ -L${OBJ} $(LDFLAGS) $(addprefix ${OBJ}, $^) + +test_local0.o: test_local0.c + mkdir -p ${OBJ} + $(CC) -c $< -o ${OBJ}$@ $(CFLAGS) + +test_local1.x: test_local1.o + mkdir -p ${BIN} + $(CC) -o ${BIN}$@ -L${OBJ} $(LDFLAGS) $(addprefix ${OBJ}, $^) + +test_local1.o: test_local1.c + mkdir -p ${OBJ} + $(CC) -c $< -o ${OBJ}$@ $(CFLAGS) + +test_numparts1.x: test_numparts1.o + mkdir -p ${BIN} + $(CC) -o ${BIN}$@ -L${OBJ} $(LDFLAGS) $(addprefix ${OBJ}, $^) + +test_numparts1.o: test_numparts1.c + mkdir -p ${OBJ} + $(CC) -c $< -o ${OBJ}$@ $(CFLAGS) + +test_numparts0.x: test_numparts0.o + mkdir -p ${BIN} + $(CC) -o ${BIN}$@ -L${OBJ} $(LDFLAGS) $(addprefix ${OBJ}, $^) + +test_numparts0.o: test_numparts0.c + mkdir -p ${OBJ} + $(CC) -c $< -o ${OBJ}$@ $(CFLAGS) + +test_order0.x: test_order0.o + mkdir -p ${BIN} + $(CC) -o ${BIN}$@ -L${OBJ} $(LDFLAGS) $(addprefix ${OBJ}, $^) + +test_order0.o: test_order0.c + mkdir -p ${OBJ} + $(CC) -c $< -o ${OBJ}$@ $(CFLAGS) + +test_parrived0.x: test_parrived0.o + mkdir -p ${BIN} + $(CC) -o ${BIN}$@ -L${OBJ} $(LDFLAGS) $(addprefix ${OBJ}, $^) + +test_parrived0.o: test_parrived0.c + mkdir -p ${OBJ} + $(CC) -c $< -o ${OBJ}$@ $(CFLAGS) + +test_parrived1.x: test_parrived1.o + mkdir -p ${BIN} + $(CC) -o ${BIN}$@ -L${OBJ} $(LDFLAGS) $(addprefix ${OBJ}, $^) + +test_parrived1.o: test_parrived1.c + mkdir -p ${OBJ} + $(CC) -c $< -o ${OBJ}$@ $(CFLAGS) + +test_parrived2.x: test_parrived2.o + mkdir -p ${BIN} + $(CC) -o ${BIN}$@ -L${OBJ} $(LDFLAGS) $(addprefix ${OBJ}, $^) + +test_parrived2.o: test_parrived2.c + mkdir -p ${OBJ} + $(CC) -c $< -o ${OBJ}$@ $(CFLAGS) + +test_partitions0.x: test_partitions0.o + mkdir -p ${BIN} + $(CC) -o ${BIN}$@ -L${OBJ} $(LDFLAGS) $(addprefix ${OBJ}, $^) + +test_partitions0.o: test_partitions0.c + mkdir -p ${OBJ} + $(CC) -c $< -o ${OBJ}$@ $(CFLAGS) + +test_partitions1.x: test_partitions1.o + mkdir -p ${BIN} + $(CC) -o ${BIN}$@ -L${OBJ} $(LDFLAGS) $(addprefix ${OBJ}, $^) + +test_partitions1.o: test_partitions1.c + mkdir -p ${OBJ} + $(CC) -c $< -o ${OBJ}$@ $(CFLAGS) + +test_partitions2.x: test_partitions2.o + mkdir -p ${BIN} + $(CC) -o ${BIN}$@ -L${OBJ} $(LDFLAGS) $(addprefix ${OBJ}, $^) + +test_partitions2.o: test_partitions2.c + mkdir -p ${OBJ} + $(CC) -c $< -o ${OBJ}$@ $(CFLAGS) + +test_partitions3.x: test_partitions3.o + mkdir -p ${BIN} + $(CC) -o ${BIN}$@ -L${OBJ} $(LDFLAGS) $(addprefix ${OBJ}, $^) + +test_partitions3.o: test_partitions3.c + mkdir -p ${OBJ} + $(CC) -c $< -o ${OBJ}$@ $(CFLAGS) + +test_pready0.x: test_pready0.o + mkdir -p ${BIN} + $(CC) -o ${BIN}$@ -L${OBJ} $(LDFLAGS) $(addprefix ${OBJ}, $^) + +test_pready0.o: test_pready0.c + mkdir -p ${OBJ} + $(CC) -c $< -o ${OBJ}$@ $(CFLAGS) + +test_pready1.x: test_pready1.o + mkdir -p ${BIN} + $(CC) -o ${BIN}$@ -L${OBJ} $(LDFLAGS) $(addprefix ${OBJ}, $^) + +test_pready1.o: test_pready1.c + mkdir -p ${OBJ} + $(CC) -c $< -o ${OBJ}$@ $(CFLAGS) + +test_pready2.x: test_pready2.o + mkdir -p ${BIN} + $(CC) -o ${BIN}$@ -L${OBJ} $(LDFLAGS) $(addprefix ${OBJ}, $^) + +test_pready2.o: test_pready2.c + mkdir -p ${OBJ} + $(CC) -c $< -o ${OBJ}$@ $(CFLAGS) + +test_pready3.x: test_pready3.o + mkdir -p ${BIN} + $(CC) -o ${BIN}$@ -L${OBJ} $(LDFLAGS) $(addprefix ${OBJ}, $^) + +test_pready3.o: test_pready3.c + mkdir -p ${OBJ} + $(CC) -c $< -o ${OBJ}$@ $(CFLAGS) + +test_pready4.x: test_pready4.o + mkdir -p ${BIN} + $(CC) -o ${BIN}$@ -L${OBJ} $(LDFLAGS) $(addprefix ${OBJ}, $^) + +test_pready4.o: test_pready4.c + mkdir -p ${OBJ} + $(CC) -c $< -o ${OBJ}$@ $(CFLAGS) + +test_pready_list0.x: test_pready_list0.o + mkdir -p ${BIN} + $(CC) -o ${BIN}$@ -L${OBJ} $(LDFLAGS) $(addprefix ${OBJ}, $^) + +test_pready_list0.o: test_pready_list0.c + mkdir -p ${OBJ} + $(CC) -c $< -o ${OBJ}$@ $(CFLAGS) + +test_pready_list1.x: test_pready_list1.o + mkdir -p ${BIN} + $(CC) -o ${BIN}$@ -L${OBJ} $(LDFLAGS) $(addprefix ${OBJ}, $^) + +test_pready_list1.o: test_pready_list1.c + mkdir -p ${OBJ} + $(CC) -c $< -o ${OBJ}$@ $(CFLAGS) + +test_pready_range0.x: test_pready_range0.o + mkdir -p ${BIN} + $(CC) -o ${BIN}$@ -L${OBJ} $(LDFLAGS) $(addprefix ${OBJ}, $^) + +test_pready_range0.o: test_pready_range0.c + mkdir -p ${OBJ} + $(CC) -c $< -o ${OBJ}$@ $(CFLAGS) + +test_startall0.x: test_startall0.o + mkdir -p ${BIN} + $(CC) -o ${BIN}$@ -L${OBJ} $(LDFLAGS) $(addprefix ${OBJ}, $^) + +test_startall0.o: test_startall0.c + mkdir -p ${OBJ} + $(CC) -c $< -o ${OBJ}$@ $(CFLAGS) + +test_state0.x: test_state0.o + mkdir -p ${BIN} + $(CC) -o ${BIN}$@ -L${OBJ} $(LDFLAGS) $(addprefix ${OBJ}, $^) + +test_state0.o: test_state0.c + mkdir -p ${OBJ} + $(CC) -c $< -o ${OBJ}$@ $(CFLAGS) + +test_wildcard0.x: test_wildcard0.o + mkdir -p ${BIN} + $(CC) -o ${BIN}$@ -L${OBJ} $(LDFLAGS) $(addprefix ${OBJ}, $^) + +test_wildcard0.o: test_wildcard0.c + mkdir -p ${OBJ} + $(CC) -c $< -o ${OBJ}$@ $(CFLAGS) + +test_wildcard1.x: test_wildcard1.o + mkdir -p ${BIN} + $(CC) -o ${BIN}$@ -L${OBJ} $(LDFLAGS) $(addprefix ${OBJ}, $^) + +test_wildcard1.o: test_wildcard1.c + mkdir -p ${OBJ} + $(CC) -c $< -o ${OBJ}$@ $(CFLAGS) + +test_zerocount0.x: test_zerocount0.o + mkdir -p ${BIN} + $(CC) -o ${BIN}$@ -L${OBJ} $(LDFLAGS) $(addprefix ${OBJ}, $^) + +test_zerocount0.o: test_zerocount0.c + mkdir -p ${OBJ} + $(CC) -c $< -o ${OBJ}$@ $(CFLAGS) + +test_zerocount1.x: test_zerocount1.o + mkdir -p ${BIN} + $(CC) -o ${BIN}$@ -L${OBJ} $(LDFLAGS) $(addprefix ${OBJ}, $^) + +test_zerocount1.o: test_zerocount1.c + mkdir -p ${OBJ} + $(CC) -c $< -o ${OBJ}$@ $(CFLAGS) + +clean: + rm -f *.o + rm -f *.x + rm -f ${OBJ}*.o + rm -f ${BIN}*.x + rm -r obj + rm -r bin diff --git a/partitioned-communication/README.md b/partitioned-communication/README.md new file mode 100644 index 0000000..8ce245b --- /dev/null +++ b/partitioned-communication/README.md @@ -0,0 +1,84 @@ +## A collection of tests for the MPI 4.1 partitioned communication API + +These tests target partitioned communication as it appears in Chapter 4 of +the [MPI Specification v. 4.1](https://www.mpi-forum.org/docs/). + +The repository contains the following: +- `test_*.c` : the tests (see below) +- `runtests.py` : a simple python 3 script for executing tests and generating a report +- other misc files : skeleton makefile, license, etc. + +### runtests.py script + +The script executes each test and looks for an expected output string in its +`stderr` output. Tests pass if the expected string is found somewhere in the +output, or (optionally) on a specific line of the output. Alternatively a test +may be designed to elicit a timeout, in which case it passes if a timeout occurs. + +A report is generated in a subdirectory along with the stderr output of each test. + +Run with `-v` flag to generate progress information on `stdout`; otherwise the tests +run silently. + +See comments in script for more information. + +### Tests + +A summary of tests is given below. For each, see the comments in the source for +motivation for the test, including (when available) references to specific pages and lines in the +v4.1 MPI specification. + +Some tests are designed to elicit errors from the MPI implementation, and if no +error is produced, the test fails. In some cases the assumption that the implementation +will produce an error message may be unfounded, e.g., because performing such a check is +difficult, impacts performance, or it is really up to the user to avoid the situation. +These tests may warrant additional discussion or exclusion. + +| Test | Description | +| ---- | ---- | +| `test_cancel01.c` | Calling MPI_Cancel on an active request should cause an error; PASS = some sort of error; FAIL = unexpected timeout or incorrect/missing error. | +| `test_datatypeX.c` | Examples in chapter 4 of the specification use different combinations of sender and receiver contiguous datatypes as well as OpenMP pragmas. The datatype tests eschew the OpenMP and check just the combination of datatypes. | +| `test_datatype0.c` | Checks case where the same datatype is used on both sender and reciever, and the same number of partitions. PASS = run to completion. | +| `test_datatype1.c` | Based on example 4.3 in specification. This test has the sender and receiver use the same number of partitions. The sender uses a datatype (one per partition) while the receiver uses MPI_INT * PARTSIZE. PASS = run to completion. See also `test_datatype4.c` | +| `test_datatype2.c` | Based on example 4.3 in specification, this test has the sender use multiple partitions and the receiver use a single partition. The sender uses contiguous datatypes (one per partition) and the reciever MESSAGE_LENGTH. PASS = run to completion. See also `test_datatype4.c` | +| `test_datatype3.c` | Same as test_datatype2.c, except receiver uses a single contiguous datatype of size MESSAGE_LENGTH. See also `test_datatype5.c`, which does the same using Isend/Irecv. PASS = run to completion. | +| `test_datatype4.c` | A sanity check confirming the combination of sender-side contiguous datatype and receiver-side non-derived datatype (MPI_INT) works outside of partcomm with Isend/Irecv. PASS = run to completion | +| `test_datatype5.c` | A sanity check confirming the combination of multiple sender-side contiguous datatypes to a single receiver-side contiguous datatype works with Isend/Irecv. PASS = run to completion. | +| `test_example1a.c` | Example 1 from the 4.1 specification. PASS = run to completion | +| `test_example1b.c` | Example 1 from the 4.1 spec, except uses MPI_WAIT instead of MPI_TEST. PASS = run to completion | +| `test_example2.c` | Example 2 from the 4.1 specification. PASS = run to completion | +| `test_example3a.c` | Example 3 from the 4.1 specification, modified to not use any contiguous datatypes, use MPI_INT, and make the number of partitions the same on both sender and receiver. PASS = run to completion. | +| `test_example3b.c` | Example 3 from the 4.1 specification, modified to use MPI_INT and not use any contiguous datatypes. Uses multiple partitions on sender and one on receiver. | +| `test_example3c.c` | Example 3 from the 4.1 specification, modified to use MPI_INT. PASS = run to completion. See also `test_datatype[2,3].c` | +| `test_free0.c` | Calling MPI_Free on an active request should cause an error; PASS = some sort of error (TBD); FAIL = unexpected timeout or incorrect/missing error | +| `test_init0.c` | Tries to match a PSEND_INIT with an IRECV. PASS = expected timeout. | +| `test_init1.c` | Tries to match an ISEND with a PRECV_INIT. PASS = expected timeout. | +| `test_init2.c` | Tries to match a persistent send init (MPI_SEND_INIT) with a partcomm recv init (PRECV_INIT). PASS = expected timeout. | +| `test_local0.c` | Confirms partcomm calls are local, so the sender can get to test/wait before the receiver even calls PRECV_INIT PASS = runs to completion. | +| `test_local1.c` | Confirms partcomm calls are local, so the receiver can get to test/wait before the sender even calls PSEND_INIT. PASS = runs to completion | +| `test_numparts0.c` | Confirms a sender can have more partitions than a receiver. PASS = runs to completion. | +| `test_numparts1.c` | Confirms a receiver can have more partitions than a sender. PASS = runs to completion. | +| `test_order0.c` | Confirms the order in which partcomm init calls are made is the order they are matched (and hence filled). PASS = runs to completion; FAIL = likely fails check of received message contents | +| `test_parrived0.c` | Confirms PARRIVED works. Also (likely) calls PARRIVED more than once on the same partition. PASS = run to completion | +| `test_parrived1.c` | Confirms PARRIVED works on a request that has never been used (an inactive request). PASS = run to completion | +| `test_parrived2.c` | Tries to call PARRIVED on a request that does not correspond to a partitioned receive operation. PASS = some sort of error | +| `test_partitions0.c` | Tries to use PSEND_INIT with zero partitions. PASS = some sort of error | +| `test_partitions1.c` | Tries to use PSEND_INIT with negative partitions. PASS = some sort of error | +| `test_partitions2.c` | Tries to use PRECV_INIT with zero partitions. PASS = some sort of error | +| `test_partitions3.c` | Tries to use PRECV_INIT with negative partitions. PASS = some sort of error | +| `test_pready0.c` | Tries to call PREADY on a partition whose index is greater than the largest valid index. PASS = some sort of error | +| `test_pready1.c` | Tries to call PREADY on a partition whose index is equal to the number of partitions (so is 1 beyond the last valid index). PASS = some sort of error | +| `test_pready2.c` | Tries to call PREADY on a partition whose index is negative. PASS = some sort of error | +| `test_pready3.c` | Tries to call PREADY on a partition that has already been marked ready by PREADY. PASS = some sort of error | +| `test_pready4.c` | Tries to call PREADY on a request object that does not correspond to a partitioned send operation. PASS = some sort of error | +| `test_pready_range0.c` | Uses PREADY_RANGE in a standard way; PASS = successfully runs to completion | +| `test_pready_list0.c` | Uses PREADY_LIST in a standard way; PASS = runs to completion | +| `test_pready_list1.c` | Uses PREADY_LIST with the same partition appearing more than once. See also test_pready3.c. PASS = some sort of error | +| `test_startall0.c` | Sets up multiple PSEND_INIT + PRECV_INIT pairs using different tags and does partcomm for each. PASS = runs to completion. | +| `test_state0.c` | Confirms that the request status is reset when multiple rounds of START/PREADY/TEST are used. See also [OMPI Issue #12328](https://github.com/open-mpi/ompi/issues/12328) PASS = runs to completion. | +| `test_wildcard0.c` | Tries to use MPI_ANY_SOURCE with PRECV_INIT. PASS = an error of some sort | +| `test_wildcard0.c` | Tries to use MPI_ANY_SOURCE with PRECV_INIT. PASS = an error of some sort | +| `test_zerocount0.c` | Confirms PSEND_INIT and PRECV_INIT can use 0 partitions with a greater-than-zero count. PASS = runs to completion | +| `test_zerocount1.c` | Confirms PSEND_INIT and PRECV_INIT can use greater-than-zero partitions with a zero count. PASS = runs to completion | + + diff --git a/partitioned-communication/runtests.py b/partitioned-communication/runtests.py new file mode 100644 index 0000000..9b8413d --- /dev/null +++ b/partitioned-communication/runtests.py @@ -0,0 +1,331 @@ +# Copyright 2024 National Technology & Engineering Solutions of Sandia, LLC +# (NTESS). Under the terms of Contract DE-NA0003525 with NTESS, the U.S. +# Government retains certain rights in this software. + +# A simple python script for running unit tests and checking their outputs +# +# The `tests` dict (below) specifies which tests are to be run and how the +# stderr output of each test is to be evaluated. Tests can pass because +# (i) an expected string was found in the output (or, optionally, on a specific +# line of the output), or (ii) the test was expected to timeout and it did. +# +# Some tests are written to elicit an error message from the MPI implementation. Because it may not +# be known a priori what this message is, the text variable TBD_ERROR is used +# as the matching string, with the expectation it will be replaced with the +# actual error message when it is available. Below, tests that contain TBD_ERROR +# are tests that fail because no error is reported by OMPI. +# +# All tests are written to use stderr, and assume OMPI error messages will appear on stderr; stdout is ignored. +# +# The script assumes if a segmentation fault occurs in the MPI implementation, it is an error. +# Segmentation faults can generate stderr outputs that are not readable by Python in text mode (only +# in binary mode). Therefore, the script tests if the output is readable in text mode; if it is not, it +# reports the test as failing and notes the file was not readable. +# +# + +import argparse +import os +import subprocess +import sys +import threading +import time + +# Use the following as placeholder text for tests designed to elicit an error message. +# When the specific error message is known, replace. +TBD_ERROR = "UNKNOWN ERROR MESSAGE EXPECTED" + +# For tests designed to elicit a timeout, use the following matching text: +TIMEOUT_EXPECTED = "__TIMEOUT_EXPECTED__" + +# The following variables are default text for generating the report +TIMEOUT_EXPECTED_AND_FOUND = "Timeout expected and did occur" +TIMEOUT_EXPECTED_AND_NOT_FOUND = "A timeout was expected but the test terminated for some other reason." +TIMEOUT_UNEXPECTED_AND_FOUND = "No timeout was expected but test terminated due to timeout" +TIMEOUT_UNEXPECTED_AND_NOT_FOUND = "This outcome should not arise" + +# subdirectory where tests are located relative to this script +BIN = "./bin/" + +# Dict of tests to run along with the string that, if found in the ouput, means the test passes. +# +# The tuple is the string to be matched, followed by the line on which it is expected. Use "*" to allow the +# matching string to appear on any line. +# +# TBD_ERROR is used as the expected string when a test is supposed to elicit an error but we do not +# have a proper output string to test for (e.g., if the test fails to error). +# +# If a timeout is the expected outcome, then use the TIMEOUT_EXPECTED variable for the match string. +# +tests = { + "test_cancel0.x": (TBD_ERROR,"*"), + "test_datatype0.x": ("END", "1"), + "test_datatype1.x": ("END", "1"), + "test_datatype2.x": ("END", "1"), + "test_datatype3.x": ("END", "1"), + "test_datatype4.x": ("END", "1"), + "test_datatype5.x": ("END", "1"), + "test_example1a.x": ("END", "1"), + "test_example1b.x": ("END", "1"), + "test_example2.x": ("END", "1"), + "test_example3a.x": ("END", "1"), + "test_example3b.x": ("END", "1"), + "test_example3c.x": ("END", "1"), + "test_free0.x": (TBD_ERROR, "*"), + "test_init0.x": (TIMEOUT_EXPECTED, "*"), + "test_init1.x": (TIMEOUT_EXPECTED, "*"), + "test_init2.x": (TIMEOUT_EXPECTED, "*"), + "test_local0.x" : ("END", "1"), + "test_local1.x" : ("END", "1"), + "test_numparts0.x" : ("END", "1"), + "test_numparts1.x" : ("END", "1"), + "test_order0.x" : ("END", "1"), + "test_parrived0.x" : ("END", "*"), + "test_parrived1.x" : ("END", "*"), + "test_parrived2.x" : ("*** An error occurred in MPI_Parrived", "*"), + "test_partitions0.x" : (TBD_ERROR, "*"), + "test_partitions1.x" : (TBD_ERROR, "*"), + "test_partitions2.x" : (TBD_ERROR, "*"), + "test_partitions3.x" : (TBD_ERROR, "*"), + "test_pready0.x" : (TBD_ERROR, "*"), + "test_pready1.x" : (TBD_ERROR, "*"), + "test_pready2.x" : (TBD_ERROR, "*"), + "test_pready3.x" : (TBD_ERROR, "*"), + "test_pready4.x" : ("MPI_ERR_REQUEST: invalid request", "*"), + "test_pready_list0.x": ("END", "1"), + "test_pready_list1.x": (TBD_ERROR, "*"), + "test_pready_range0.x": ("END", "1"), + "test_startall0.x" : ("END", "*"), + "test_state0.x" : (TBD_ERROR, "*"), + "test_wildcard0.x" : (TBD_ERROR, "*"), + "test_wildcard1.x" : (TBD_ERROR, "*"), + "test_zerocount0.x" : ("END", "*"), + "test_zerocount1.x" : ("END", "*") + } + +# for colored verbose output +class txt_colors: + PASS = '\033[92m' + FAIL = '\033[91m' + ENDC = '\033[0m' + +# test output is directed here +out_stderr = ".stderr" +out_stdout = ".stdout" + +# prefix of output directory (date/time will be appended) +outdir = "results_" + +# name of report file +reportfn = "report.txt" + +# for timeout +timeout_thread_abort = False +timeout_occurred = False +DEFAULT_TIMEOUT_SECONDS = 25 + +# for stats +passed = 0 +failed = 0 + +def error(msg): + print("ERROR: {0}".format(msg), file=sys.stderr) + exit(1) + +# if the test passed +def report_success(outfile, test, msg=""): + global passed + passed += 1 + print("{0} : passed : {1}".format(test, msg), file=outfile) + +# if the test failed +def report_failure(outfile, test, msg=""): + global failed + failed += 1 + print("{0} : FAILED : {1}".format(test, msg), file=outfile) + +def get_process_id(name): + child = subprocess.Popen(['pgrep', '-f', name], stdout=subprocess.PIPE, shell=False) + response = child.communicate()[0] + return [int(pid) for pid in response.split()] + +# function for timeout thread +def timeout_function(seconds, t): + global timeout_occurred + timeout_occurred = False + count = 0 + pid = get_process_id(t) + while (not pid) and (count < 5): + pid = get_process_id(t) + t_start = int(time.time()) + while ((int(time.time()) - t_start) < seconds): + if timeout_thread_abort: + return + timeout_occurred = True + subprocess.run("kill -9 {0}".format(pid[0]), stderr=outfile_stderr, stdout=subprocess.DEVNULL, shell=True) + return + +# check if output file to be checked has only text +# When a segfault occurs, it can generate outout that cannot be decoded to text +def file_is_readable(infn): + try: + infile = open(infn, "r") + except: + error("Could not open file {0}".format(infn)) + try: + for line in infile: + print("", end='') + except: + return False + return True + +# helper to avoid duplicating code +def verbose_pass_fail(test_passed): + if test_passed: + print(f"{txt_colors.PASS}PASSED{txt_colors.ENDC}", file=sys.stdout) + else: + print(f"{txt_colors.FAIL}FAILED{txt_colors.ENDC}", file=sys.stdout) + + +if __name__ == "__main__": + + parser = argparse.ArgumentParser() + parser.add_argument("-v", "--verbose", action="store_true", dest="verbose", help="Verbose mode") + + args = parser.parse_args() + verbose = args.verbose + + tstr = time.strftime("%Y%m%d-%H%M%S") + outdir = outdir + tstr + os.mkdir(outdir) + outdir = "./" + outdir + + reportfn = outdir + "/" + reportfn + try: + reportfile = open(reportfn, "w") + except: + error("Could not open file {0} for writing A".format(reportfn)) + + total_tests = len(tests) + cur_test = 0 + + # add some info to the report + print("", file=reportfile) + print("---- Tests start: {0} ----".format(tstr), file=reportfile) + print("Tests (num = {0}):".format(total_tests), file=reportfile) + for t in tests: + print(" {0}".format(t), file=reportfile) + print(file=reportfile) + + tt_start = time.time() + + for t in tests: + cur_test += 1 + if verbose: + print("{0} / {1} : Running test: {2} --> ".format(cur_test, total_tests, t),end='', file=sys.stdout, flush=True) + command = "mpirun -np 2 --npernode 1 " + BIN + t + tmp_out_stderr = outdir + "/" + t + out_stderr + try: + outfile_stderr = open(tmp_out_stderr, 'w') + except: + error("Could not open file {0} for writing B".format(tmp_out_stderr)) + + timeout_thread_abort = False + timeout_thread = threading.Thread(target=timeout_function, args=(DEFAULT_TIMEOUT_SECONDS,t,)) + + timeout_thread.start() + subprocess.run(command, stderr=outfile_stderr, stdout=subprocess.DEVNULL, shell=True) + if not timeout_occurred: + timeout_thread_abort = True + outfile_stderr.close() + + match_string = tests[t][0] + testline = tests[t][1] + + test_passed = False + + # Confirm the file can be read as text + # If a segmentation fault occurs in the MPI implementation, it can generate contents that are + # only readable in binary mode, which this code cannot test. So if it is not readable, its an + # error. + is_readable = file_is_readable(tmp_out_stderr) + if not is_readable: + report_failure(reportfile, t, "Output is not readable as text. Check .stderr output") + if verbose: + verbose_pass_fail(test_passed) + continue + + testfile = open(tmp_out_stderr,'r') + + curline = 0 + foundline = False + # timeout occurred but not expected + if timeout_occurred and (match_string != TIMEOUT_EXPECTED): + report_failure(reportfile, t, TIMEOUT_UNEXPECTED_AND_FOUND) + foundline = True # to avoid another failure message + # timeout occured and expected + elif timeout_occurred and (match_string == TIMEOUT_EXPECTED): + report_success(reportfile, t, TIMEOUT_EXPECTED_AND_FOUND) + foundline = True # to avoid another failure message + test_passed = True + # timeout did not occur but was expected + elif (not timeout_occurred) and (match_string == TIMEOUT_EXPECTED): + report_failure(reportfile, t, TIMEOUT_EXPECTED_AND_NOT_FOUND) + foundline = True # to avoid another failure message + elif testline == "*": + for line in testfile: + curline += 1 + line = line.rstrip() + if match_string in line: + foundline = True + report_success(reportfile, t, "Match found on line {0}".format(curline)) + test_passed = True + break + else: # look only at specified line + try: + testline = int(testline) + except: + error("Line specified for test {0} is neither an int nor *") + for line in testfile: + curline += 1 + line = line.rstrip() + if curline == testline: + foundline = True + if match_string in line: + report_success(reportfile, t, "Match found on line {0}".format(curline)) + test_passed = True + else: + report_failure(reportfile, t, "On line {0}: Expected: {1} Found: {2}".format(testline, match_string, line)) + break + + # If no match found, provide more information + if not foundline: + if testline == "*": + report_failure(reportfile, t, "Expected text not found on any line. ({0})".format(match_string)) + else: + report_failure(reportfile, t, "Expected to test on line {0}, but stderr output file has only {1} lines.".format(testline, curline)) + + if verbose: + verbose_pass_fail(test_passed) + + time.sleep(7) + + tt_end = time.time() + + print("", file=reportfile) + print("", file=reportfile) + if (total_tests != (passed + failed)): + print("Note: total_tests (= {0}) did not equal passed + failed (= {1})".format(total_tests, passed, failed), file=reportfile) + passed_total = float(passed)/float(total_tests) + failed_total = float(failed)/float(total_tests) + print("{0} of {1} tests passed ({2:.2f}%)".format(passed, total_tests, passed_total*100), file=reportfile) + print("{0} of {1} tests failed ({2:.2f}%)".format(failed, total_tests, failed_total*100), file=reportfile) + print("", file=reportfile) + print("---- Tests end: {0} ----".format(time.strftime("%Y%m%d-%H%M%S")), file=reportfile) + print("---- Total time required to run tests: {0:.2f} seconds".format(tt_end - tt_start), file=reportfile) + reportfile.close() + + if verbose: + print("{0} of {1} tests passed ({2:.2f}%)".format(passed, total_tests, passed_total*100), file=sys.stdout) + print("{0} of {1} tests failed ({2:.2f}%)".format(failed, total_tests, failed_total*100), file=sys.stdout) + diff --git a/partitioned-communication/test_cancel0.c b/partitioned-communication/test_cancel0.c new file mode 100644 index 0000000..2c31fa9 --- /dev/null +++ b/partitioned-communication/test_cancel0.c @@ -0,0 +1,98 @@ +// Copyright 2024 National Technology & Engineering Solutions of Sandia, LLC +// (NTESS). Under the terms of Contract DE-NA0003525 with NTESS, the U.S. +// Government retains certain rights in this software. + +/* + * P. 116, line 11: Freeing or canceling a partitioned communication request that is active (i.e., initialized and started) and not completed is erroneous. + * + * Expected outcome: Error message due to calling MPI_Cancel on a request that is active + * If test times out, then the error was not caught by the MPI library + * + */ + +#include "test_common.h" + +#define PARTITIONS 8 +#define COUNT 5 + +int main(int argc, char *argv[]) { + + int message[PARTITIONS*COUNT]; + + MPI_Count partitions = PARTITIONS; + + int source = 0, dest = 1, tag = 1, flag = 0; + int myrank, i, j; + int provided; + + for (i = 0; i < PARTITIONS * COUNT; ++i) message[i] = 0; + + MPI_Request request; + MPI_Init_thread(&argc, &argv, MPI_THREAD_SERIALIZED, &provided); + if (provided < MPI_THREAD_SERIALIZED) MPI_Abort(MPI_COMM_WORLD , EXIT_FAILURE); + MPI_Comm_rank(MPI_COMM_WORLD , &myrank); + + if (0 == myrank) { + + fprintf(stderr, "Rank 0 initializing and starting Psend\n"); + CHECK_RETVAL(MPI_Psend_init(message, partitions, COUNT, MPI_INT, dest, tag, MPI_COMM_WORLD , MPI_INFO_NULL , &request)); + MPI_Start(&request); + + /* This should cause an error. See 4.1 p. 102 for info on cancel */ + fprintf(stderr, "Rank 0 canceling request (erroneous)\n"); + CHECK_RETVAL(MPI_Cancel(&request)); + + /* Cancel is followed by wait, which should be local. If cancel ignored, then may hang here because nothing has been marked ready */ + fprintf(stderr, "Rank 0 waiting on cancelled request\n"); + CHECK_RETVAL(MPI_Wait(&request, MPI_STATUS_IGNORE)); + + /* Now we the request is inactive and we can use it again or free it */ + MPI_Request_free(&request); + + /* Rest of code is the standard Psend/Precv example. If the MPI_Request_free is ignored and no error reported, + * then the remainder of the code may hang because the first Psend is never completed, and the destination is testing on it + */ + + fprintf(stderr, "Rank 0 starting second Psend\n"); + CHECK_RETVAL(MPI_Psend_init(message, partitions, COUNT, MPI_INT, dest, tag, MPI_COMM_WORLD , MPI_INFO_NULL , &request)); + CHECK_RETVAL(MPI_Start(&request)); + + /* Fill the sending partitions and mark as ready as each is filled */ + for (i = 0; i < partitions; ++i) { + for (j = 0; j < COUNT; ++j) message[j+(COUNT*i)] = j+(COUNT*i); + CHECK_RETVAL(MPI_Pready(i, request)); + } + + while (!flag) { + MPI_Test(&request, &flag, MPI_STATUS_IGNORE); + } + + MPI_Request_free(&request); + + } + else if (1 == myrank) { + MPI_Precv_init(message, partitions, COUNT, MPI_INT, source, tag, MPI_COMM_WORLD , MPI_INFO_NULL , &request); + MPI_Start(&request); + while (!flag) { + MPI_Test(&request, &flag, MPI_STATUS_IGNORE); + } + + MPI_Request_free(&request); + + /* all partitions received; check contents */ + for (i = 0; i < PARTITIONS*COUNT; ++i) { + if (message[i] != i) { + fprintf(stderr, "ERROR: Contents received do not match contents sent (expected %d, found %d).\n",i,message[i]); + MPI_Abort(MPI_COMM_WORLD, 1); + } + } + } + + MPI_Barrier(MPI_COMM_WORLD); + if (0 == myrank) {TEST_RAN_TO_COMPLETION();} + + MPI_Finalize(); + + return 0; +} + diff --git a/partitioned-communication/test_common.h b/partitioned-communication/test_common.h new file mode 100644 index 0000000..b5d7cd3 --- /dev/null +++ b/partitioned-communication/test_common.h @@ -0,0 +1,24 @@ +// Copyright 2024 National Technology & Engineering Solutions of Sandia, LLC +// (NTESS). Under the terms of Contract DE-NA0003525 with NTESS, the U.S. +// Government retains certain rights in this software. + +#ifndef TEST_COMMON_H +#define TEST_COMMON_H + +#include +#include +#include "mpi.h" + +#define CHECK_RETVAL(x) do { \ + int retval = (x); \ + if (retval != MPI_SUCCESS) { \ + fprintf(stderr, "Error: %s returned %d (expected %d)\n", #x, retval, MPI_SUCCESS); \ + MPI_Abort(MPI_COMM_WORLD, 999); \ + } \ + } while (0) + +#define TEST_RAN_TO_COMPLETION() do { \ + fprintf(stderr, "END\n"); \ + } while (0) + +#endif diff --git a/partitioned-communication/test_datatype0.c b/partitioned-communication/test_datatype0.c new file mode 100644 index 0000000..1c932ff --- /dev/null +++ b/partitioned-communication/test_datatype0.c @@ -0,0 +1,95 @@ +// Copyright 2024 National Technology & Engineering Solutions of Sandia, LLC +// (NTESS). Under the terms of Contract DE-NA0003525 with NTESS, the U.S. +// Government retains certain rights in this software. + +/* + * The examples in chapter 4 of the specification use different combinations of + * sender and receiver contiguous datatypes that for testing purposes should be + * considered outside the use of OpenMP pragmas. + * + * This test uses the same datatype on sender and receiver, and the same + * number of partitions. + * + * Expected outcome: PASS + * + */ + +#include "test_common.h" + +#define PARTITIONS 64 +#define PARTLENGTH 16 +#define MESSAGE_LENGTH PARTITIONS*PARTLENGTH + +int main(int argc, char *argv[]) { + + int message[MESSAGE_LENGTH]; + int partitions = PARTITIONS; + int partlength = PARTLENGTH; + + int count = 1, source = 0, dest = 1, tag = 1, flag = 0; + int i, j; + int myrank; + int provided; + int my_thread_id; + + MPI_Request request; + MPI_Status status; + MPI_Info info = MPI_INFO_NULL; + MPI_Datatype xfer_type; + MPI_Init_thread(&argc, &argv, MPI_THREAD_MULTIPLE, &provided); + if (provided < MPI_THREAD_MULTIPLE) MPI_Abort(MPI_COMM_WORLD , EXIT_FAILURE); + MPI_Comm_rank(MPI_COMM_WORLD , &myrank); + + /* Sender and receiver both use the same transfer datatype */ + MPI_Type_contiguous(partlength, MPI_INT, &xfer_type); + MPI_Type_commit(&xfer_type); + + if (0 == myrank) { + + for (i = 0; i < partitions * partlength; ++i) message[i] = 100; + + MPI_Psend_init(message, partitions, count, xfer_type, dest, tag, MPI_COMM_WORLD, info, &request); + MPI_Start(&request); + + for (i = 0; i < partitions; ++i) { + for (j = 0; j < partlength; ++j) { + message[j + (i * partlength)] = j + (i * partlength); + } + MPI_Pready(i, request); + } + + while(!flag) { + MPI_Test(&request, &flag, MPI_STATUS_IGNORE); + } + + MPI_Request_free(&request); + + } else if (1 == myrank) { + for (i = 0; i < partitions * partlength; ++i) message[i] = 101; + + MPI_Precv_init(message, partitions, count, xfer_type, source, tag, MPI_COMM_WORLD, info, &request); + MPI_Start(&request); + + while(!flag) { + MPI_Test(&request, &flag, MPI_STATUS_IGNORE); + } + + MPI_Request_free(&request); + + /* all partitions received; check contents */ + for (i = 0; i < MESSAGE_LENGTH; ++i) { + if (message[i] != i) { + fprintf(stderr, "ERROR: Contents received do not match contents sent (expected %d, found %d).\n",i,message[i]); + MPI_Abort(MPI_COMM_WORLD, 1); + } + } + } + + MPI_Barrier(MPI_COMM_WORLD); + + if (myrank == 0) {TEST_RAN_TO_COMPLETION();} + + MPI_Finalize(); + return 0; + +} diff --git a/partitioned-communication/test_datatype1.c b/partitioned-communication/test_datatype1.c new file mode 100644 index 0000000..03e7578 --- /dev/null +++ b/partitioned-communication/test_datatype1.c @@ -0,0 +1,95 @@ +// Copyright 2024 National Technology & Engineering Solutions of Sandia, LLC +// (NTESS). Under the terms of Contract DE-NA0003525 with NTESS, the U.S. +// Government retains certain rights in this software. + +/* + * The examples in chapter 4 of the specification use different combinations of + * sender and receiver contiguous datatypes that for testing purposes should be + * considered outside the use of OpenMP pragmas. + * + * Example 4.3 uses a contiguous send datatype (one per partition) to a receiver using a single partition of + * MPI_DOUBLEs. This test simplifies by removing OpenMP code and makes the number of partitions + * the same. + * + * Expected outcome: PASS + * + */ + +#include "test_common.h" + +#define PARTITIONS 64 +#define PARTLENGTH 16 +#define MESSAGE_LENGTH PARTITIONS*PARTLENGTH + +int main(int argc, char *argv[]) { + + int message[MESSAGE_LENGTH]; + int partitions = PARTITIONS; + int partlength = PARTLENGTH; + + int count = 1, source = 0, dest = 1, tag = 1, flag = 0; + int i, j; + int myrank; + int provided; + int my_thread_id; + + MPI_Request request; + MPI_Status status; + MPI_Info info = MPI_INFO_NULL; + MPI_Datatype send_type; + MPI_Init_thread(&argc, &argv, MPI_THREAD_MULTIPLE, &provided); + if (provided < MPI_THREAD_MULTIPLE) MPI_Abort(MPI_COMM_WORLD , EXIT_FAILURE); + MPI_Comm_rank(MPI_COMM_WORLD , &myrank); + + /* Sender uses this datatype */ + MPI_Type_contiguous(partlength, MPI_INT, &send_type); + MPI_Type_commit(&send_type); + + if (0 == myrank) { + + for (i = 0; i < partitions * partlength; ++i) message[i] = 100; + + MPI_Psend_init(message, partitions, count, send_type, dest, tag, MPI_COMM_WORLD, info, &request); + MPI_Start(&request); + + for (i = 0; i < partitions; ++i) { + for (j = 0; j < partlength; ++j) { + message[j + (i * partlength)] = j + (i * partlength); + } + MPI_Pready(i, request); + } + + while(!flag) { + MPI_Test(&request, &flag, MPI_STATUS_IGNORE); + } + + MPI_Request_free(&request); } + + else if (1 == myrank) { + for (i = 0; i < partitions * partlength; ++i) message[i] = 101; + + MPI_Precv_init(message, partitions, partlength, MPI_INT, source, tag, MPI_COMM_WORLD, info, &request); + MPI_Start(&request); + + while(!flag) { + MPI_Test(&request, &flag, MPI_STATUS_IGNORE); + } + + MPI_Request_free(&request); + + /* all partitions received; check contents */ + for (i = 0; i < MESSAGE_LENGTH; ++i) { + if (message[i] != i) { + fprintf(stderr, "ERROR: Contents received do not match contents sent (expected %d, found %d).\n",i,message[i]); + MPI_Abort(MPI_COMM_WORLD, 1); + } + } + } + + MPI_Barrier(MPI_COMM_WORLD); + if (0 == myrank) {TEST_RAN_TO_COMPLETION();} + + MPI_Finalize(); + return 0; + +} diff --git a/partitioned-communication/test_datatype2.c b/partitioned-communication/test_datatype2.c new file mode 100644 index 0000000..ee6b9c2 --- /dev/null +++ b/partitioned-communication/test_datatype2.c @@ -0,0 +1,96 @@ +// Copyright 2024 National Technology & Engineering Solutions of Sandia, LLC +// (NTESS). Under the terms of Contract DE-NA0003525 with NTESS, the U.S. +// Government retains certain rights in this software. + +/* + * The examples in chapter 4 of the specification use different combinations of + * sender and receiver contiguous datatypes that for testing purposes should be + * considered outside the use of OpenMP pragmas. + * + * Example 4.3 uses a contiguous send datatype (one per partition) to a receiver using a single partition of + * MPI_DOUBLEs. This test simplifies by using MPI_INT and removing OpenMP + * + * Expected outcome: PASS + * + */ + +#include "test_common.h" + +#define PARTITIONS 64 +#define PARTLENGTH 16 +#define MESSAGE_LENGTH PARTITIONS*PARTLENGTH + +int main(int argc, char *argv[]) { + + int message[MESSAGE_LENGTH]; + int send_partitions = PARTITIONS; + int send_partlength = PARTLENGTH; + int recv_partitions = 1; + int recv_partlength = MESSAGE_LENGTH; + + int count = 1, source = 0, dest = 1, tag = 1, flag = 0; + int i, j; + int myrank; + int provided; + int my_thread_id; + + MPI_Request request; + MPI_Status status; + MPI_Info info = MPI_INFO_NULL; + MPI_Datatype send_type; + MPI_Init_thread(&argc, &argv, MPI_THREAD_MULTIPLE, &provided); + if (provided < MPI_THREAD_MULTIPLE) MPI_Abort(MPI_COMM_WORLD , EXIT_FAILURE); + MPI_Comm_rank(MPI_COMM_WORLD , &myrank); + + /* Sender uses this datatype */ + MPI_Type_contiguous(send_partlength, MPI_INT, &send_type); + MPI_Type_commit(&send_type); + + if (0 == myrank) { + + for (i = 0; i < send_partitions * send_partlength; ++i) message[i] = 100; + + MPI_Psend_init(message, send_partitions, count, send_type, dest, tag, MPI_COMM_WORLD, info, &request); + MPI_Start(&request); + + for (i = 0; i < send_partitions; ++i) { + for (j = 0; j < send_partlength; ++j) { + message[j + (i * send_partlength)] = j + (i * send_partlength); + } + MPI_Pready(i, request); + } + + while(!flag) { + MPI_Test(&request, &flag, MPI_STATUS_IGNORE); + } + + MPI_Request_free(&request); + + } else if (1 == myrank) { + for (i = 0; i < recv_partitions * recv_partlength; ++i) message[i] = 101; + + MPI_Precv_init(message, recv_partitions, recv_partlength, MPI_INT, source, tag, MPI_COMM_WORLD, info, &request); + MPI_Start(&request); + + while(!flag) { + MPI_Test(&request, &flag, MPI_STATUS_IGNORE); + } + + MPI_Request_free(&request); + + /* all partitions received; check contents */ + for (i = 0; i < MESSAGE_LENGTH; ++i) { + if (message[i] != i) { + fprintf(stderr, "ERROR: Contents received do not match contents sent (expected %d, found %d).\n",i,message[i]); + MPI_Abort(MPI_COMM_WORLD, 1); + } + } + } + + MPI_Barrier(MPI_COMM_WORLD); + if (0 == myrank) {TEST_RAN_TO_COMPLETION();} + + MPI_Finalize(); + return 0; + +} diff --git a/partitioned-communication/test_datatype3.c b/partitioned-communication/test_datatype3.c new file mode 100644 index 0000000..d40a14c --- /dev/null +++ b/partitioned-communication/test_datatype3.c @@ -0,0 +1,108 @@ +// Copyright 2022 National Technology & Engineering Solutions of Sandia, LLC +// (NTESS). Under the terms of Contract DE-NA0003525 with NTESS, the U.S. +// Government retains certain rights in this software. + +/* + * The examples in chapter 4 of the specification use different combinations of + * sender and receiver contiguous datatypes that for testing purposes should be + * considered outside the use of OpenMP pragmas. + * + * This test has the sender use a datatype multiple times (one per partition) and + * the receiver receive into a single partition with a single datatype. + * + * Compare with test_datatype5.c, which does the same thing with Isend/Irecv insteaad of partcomm + * + * Expected outcome: PASS + * + */ + +#include "test_common.h" + +#define PARTITIONS 64 +#define PARTLENGTH 16 +#define MESSAGE_LENGTH PARTITIONS*PARTLENGTH + +int main(int argc, char *argv[]) { + + int message[MESSAGE_LENGTH]; + // If the test seg faults, it may be a memory alignment issue + //int *message = NULL; + //if (posix_memalign((void *)&message, 32, sizeof(int) * MESSAGE_LENGTH)) { + // fprintf(stderr, "Error using posix_memalign\n"); + // exit(-1); + //} + int send_partitions = PARTITIONS; + int send_partlength = PARTLENGTH; + int recv_partitions = 1; + int recv_partlength = MESSAGE_LENGTH; + + int send_count = 1, recv_count = 1, source = 0, dest = 1, tag = 1, flag = 0; + int i, j; + int myrank; + int provided; + int my_thread_id; + + MPI_Request request; + MPI_Status status; + MPI_Info info = MPI_INFO_NULL; + MPI_Datatype send_type, recv_type; + MPI_Init_thread(&argc, &argv, MPI_THREAD_MULTIPLE, &provided); + if (provided < MPI_THREAD_MULTIPLE) MPI_Abort(MPI_COMM_WORLD , EXIT_FAILURE); + MPI_Comm_rank(MPI_COMM_WORLD , &myrank); + + /* sender uses PARTITIONS partitions each comprising one instance of a PARTLENTH contiguous datatype */ + MPI_Type_contiguous(send_partlength, MPI_INT, &send_type); + MPI_Type_commit(&send_type); + + /* receier uses 1 partition comprising one instance of a MESSAGE_LENGTH contiguous datatype */ + MPI_Type_contiguous(recv_partlength, MPI_INT, &recv_type); + MPI_Type_commit(&recv_type); + + if (0 == myrank) { + + for (i = 0; i < send_partitions * send_partlength; ++i) message[i] = 100; + + MPI_Psend_init(message, send_partitions, send_count, send_type, dest, tag, MPI_COMM_WORLD, info, &request); + MPI_Start(&request); + + for (i = 0; i < send_partitions; ++i) { + for (j = 0; j < send_partlength; ++j) { + message[j + (i * send_partlength)] = j + (i * send_partlength); + } + MPI_Pready(i, request); + } + + while(!flag) { + MPI_Test(&request, &flag, MPI_STATUS_IGNORE); + } + + MPI_Request_free(&request); + + } else if (1 == myrank) { + for (i = 0; i < send_partitions * send_partlength; ++i) message[i] = 101; + + MPI_Precv_init(message, recv_partitions, recv_count, recv_type, source, tag, MPI_COMM_WORLD, info, &request); + MPI_Start(&request); + + while(!flag) { + MPI_Test(&request, &flag, MPI_STATUS_IGNORE); + } + + MPI_Request_free(&request); + + /* all partitions received; check contents */ + for (i = 0; i < MESSAGE_LENGTH; ++i) { + if (message[i] != i) { + fprintf(stderr, "ERROR: Contents received do not match contents sent (expected %d, found %d).\n",i,message[i]); + MPI_Abort(MPI_COMM_WORLD, 1); + } + } + } + + MPI_Barrier(MPI_COMM_WORLD); + if (0 == myrank) {TEST_RAN_TO_COMPLETION();} + + MPI_Finalize(); + return 0; + +} diff --git a/partitioned-communication/test_datatype4.c b/partitioned-communication/test_datatype4.c new file mode 100644 index 0000000..ff77c85 --- /dev/null +++ b/partitioned-communication/test_datatype4.c @@ -0,0 +1,77 @@ +// Copyright 2024 National Technology & Engineering Solutions of Sandia, LLC +// (NTESS). Under the terms of Contract DE-NA0003525 with NTESS, the U.S. +// Government retains certain rights in this software. + +/* + * The examples in chapter 4 of the specification use different combinations of + * sender and receiver contiguous datatypes that for testing purposes should be + * considered outside the use of OpenMP pragmas. + * + * This test is simply a sanity check to make sure the combination of sender-side + * contiguous datatype and receiver-side non-derived datatype works with basic non-blocking send/recv. + * + * Expected outcome: PASS + * + */ + +#include "test_common.h" + +#define MESSAGE_LENGTH 256 +#define DATATYPE_LENGTH 128 + +int main(int argc, char *argv[]) { + + int message[MESSAGE_LENGTH]; + + int count = 2, source = 0, dest = 1, tag = 1, flag = 0; + int i, j; + int myrank; + int provided; + int my_thread_id; + + MPI_Request request; + + MPI_Info info = MPI_INFO_NULL; + MPI_Datatype send_type; + MPI_Init_thread(&argc, &argv, MPI_THREAD_MULTIPLE, &provided); + + if (provided < MPI_THREAD_MULTIPLE) MPI_Abort(MPI_COMM_WORLD , EXIT_FAILURE); + + MPI_Comm_rank(MPI_COMM_WORLD , &myrank); + + /* Sender uses this datatype */ + MPI_Type_contiguous(DATATYPE_LENGTH, MPI_INT, &send_type); + MPI_Type_commit(&send_type); + + if (0 == myrank) { + + for (i = 0; i < MESSAGE_LENGTH; ++i) message[i] = i; + + MPI_Isend(message, count, send_type, dest, tag, MPI_COMM_WORLD, &request); + + MPI_Wait(&request, MPI_STATUS_IGNORE); + + } else if (1 == myrank) { + + for (i = 0; i < MESSAGE_LENGTH; ++i) message[i] = 101; + + MPI_Irecv(message, MESSAGE_LENGTH, MPI_INT, source, tag, MPI_COMM_WORLD, &request); + + MPI_Wait(&request, MPI_STATUS_IGNORE); + + /* all partitions received; check contents */ + for (i = 0; i < MESSAGE_LENGTH; ++i) { + if (message[i] != i) { + fprintf(stderr, "ERROR: Contents received do not match contents sent (expected %d, found %d).\n",i,message[i]); + MPI_Abort(MPI_COMM_WORLD, 1); + } + } + } + + MPI_Barrier(MPI_COMM_WORLD); + if (0 == myrank) {TEST_RAN_TO_COMPLETION();} + + MPI_Finalize(); + return 0; + +} diff --git a/partitioned-communication/test_datatype5.c b/partitioned-communication/test_datatype5.c new file mode 100644 index 0000000..5aef3a4 --- /dev/null +++ b/partitioned-communication/test_datatype5.c @@ -0,0 +1,78 @@ +// Copyright 2024 National Technology & Engineering Solutions of Sandia, LLC +// (NTESS). Under the terms of Contract DE-NA0003525 with NTESS, the U.S. +// Government retains certain rights in this software. + +/* + * The examples in chapter 4 of the specification use different combinations of + * sender and receiver contiguous datatypes that for testing purposes should be + * considered outside the use of OpenMP pragmas. + * + * This is a sanity check to determine that a multipe counts of a sender-side + * contiguous datatype can be send to a receiver with a single contiguous datatype + * + * Expected outcome: PASS + * + */ + +#include "test_common.h" + +#define MESSAGE_LENGTH 256 +#define SEND_DATATYPE_LENGTH 64 + +int main(int argc, char *argv[]) { + + int message[MESSAGE_LENGTH]; + + int send_count = 4, recv_count = 1, source = 0, dest = 1, tag = 1, flag = 0; + int i, j; + int myrank; + int provided; + int my_thread_id; + + MPI_Request request; + + MPI_Info info = MPI_INFO_NULL; + MPI_Datatype send_type, recv_type; + MPI_Init_thread(&argc, &argv, MPI_THREAD_MULTIPLE, &provided); + + if (provided < MPI_THREAD_MULTIPLE) MPI_Abort(MPI_COMM_WORLD , EXIT_FAILURE); + + MPI_Comm_rank(MPI_COMM_WORLD , &myrank); + + MPI_Type_contiguous(SEND_DATATYPE_LENGTH, MPI_INT, &send_type); + MPI_Type_commit(&send_type); + MPI_Type_contiguous(MESSAGE_LENGTH, MPI_INT, &recv_type); + MPI_Type_commit(&recv_type); + + if (0 == myrank) { + + for (i = 0; i < MESSAGE_LENGTH; ++i) message[i] = i; + + MPI_Isend(message, send_count, send_type, dest, tag, MPI_COMM_WORLD, &request); + + MPI_Wait(&request, MPI_STATUS_IGNORE); + + } else if (1 == myrank) { + + for (i = 0; i < MESSAGE_LENGTH; ++i) message[i] = 101; + + MPI_Irecv(message, recv_count, recv_type, source, tag, MPI_COMM_WORLD, &request); + + MPI_Wait(&request, MPI_STATUS_IGNORE); + + /* all partitions received; check contents */ + for (i = 0; i < MESSAGE_LENGTH; ++i) { + if (message[i] != i) { + fprintf(stderr, "ERROR: Contents received do not match contents sent (expected %d, found %d).\n",i,message[i]); + MPI_Abort(MPI_COMM_WORLD, 1); + } + } + } + + MPI_Barrier(MPI_COMM_WORLD); + if (0 == myrank) {TEST_RAN_TO_COMPLETION();} + + MPI_Finalize(); + return 0; + +} diff --git a/partitioned-communication/test_example1a.c b/partitioned-communication/test_example1a.c new file mode 100644 index 0000000..740bcb9 --- /dev/null +++ b/partitioned-communication/test_example1a.c @@ -0,0 +1,78 @@ +// Copyright 2024 National Technology & Engineering Solutions of Sandia, LLC +// (NTESS). Under the terms of Contract DE-NA0003525 with NTESS, the U.S. +// Government retains certain rights in this software. + +/* + * This is example 4.1, p. 114-115, MPI Standard v. 4.1 + * Extended to (1) fill buffer, (2) test that correct results are received. + * + * Expected outcome: PASS + * + */ + +#include "test_common.h" + +#define PARTITIONS 8 +#define COUNT 5 + +int main(int argc, char *argv[]) { + + int message[PARTITIONS*COUNT]; + + MPI_Count partitions = PARTITIONS; + + int source = 0, dest = 1, tag = 1, flag = 0; + int myrank, i, j; + int provided; + + for (i = 0; i < PARTITIONS * COUNT; ++i) message[i] = 0; + + MPI_Request request; + MPI_Init_thread(&argc, &argv, MPI_THREAD_SERIALIZED, &provided); + if (provided < MPI_THREAD_SERIALIZED) MPI_Abort(MPI_COMM_WORLD , EXIT_FAILURE); + MPI_Comm_rank(MPI_COMM_WORLD , &myrank); + + if (0 == myrank) { + + CHECK_RETVAL(MPI_Psend_init(message, partitions, COUNT, MPI_INT, dest, tag, MPI_COMM_WORLD , MPI_INFO_NULL , &request)); + CHECK_RETVAL(MPI_Start(&request)); + + /* Fill the sending partitions and mark as ready as each is filled */ + for (i = 0; i < partitions; ++i) { + for (j = 0; j < COUNT; ++j) message[j+(COUNT*i)] = j+(COUNT*i); + CHECK_RETVAL(MPI_Pready(i, request)); + } + + while (!flag) { + CHECK_RETVAL(MPI_Test(&request, &flag, MPI_STATUS_IGNORE)); + } + + MPI_Request_free(&request); + + } + else if (1 == myrank) { + CHECK_RETVAL(MPI_Precv_init(message, partitions, COUNT, MPI_INT, source, tag, MPI_COMM_WORLD , MPI_INFO_NULL , &request)); + CHECK_RETVAL(MPI_Start(&request)); + while (!flag) { + CHECK_RETVAL(MPI_Test(&request, &flag, MPI_STATUS_IGNORE)); + } + + MPI_Request_free(&request); + + /* all partitions received; check contents */ + for (i = 0; i < PARTITIONS*COUNT; ++i) { + if (message[i] != i) { + fprintf(stderr, "ERROR: Contents received do not match contents sent (expected %d, found %d).\n",i,message[i]); + MPI_Abort(MPI_COMM_WORLD, 1); + } + } + } + + MPI_Barrier(MPI_COMM_WORLD); + if (0 == myrank) {TEST_RAN_TO_COMPLETION();} + + MPI_Finalize(); + + return 0; +} + diff --git a/partitioned-communication/test_example1b.c b/partitioned-communication/test_example1b.c new file mode 100644 index 0000000..1827221 --- /dev/null +++ b/partitioned-communication/test_example1b.c @@ -0,0 +1,75 @@ +// Copyright 2024 National Technology & Engineering Solutions of Sandia, LLC +// (NTESS). Under the terms of Contract DE-NA0003525 with NTESS, the U.S. +// Government retains certain rights in this software. + +/* + * This is example 4.1, p. 114-115, MPI Standard v. 4.1 + * Extended to (1) fill buffer, (2) test that correct results are received. + * + * This version uses MPI_Wait instead of MPI_Test, to confirm WAIT works (P. 120, line 16) + * + * Expected outcome: PASS + * + */ + +#include "test_common.h" + +#define PARTITIONS 8 +#define COUNT 5 + +int main(int argc, char *argv[]) { + + int message[PARTITIONS*COUNT]; + + MPI_Count partitions = PARTITIONS; + + int source = 0, dest = 1, tag = 1, flag = 0; + int myrank, i, j; + int provided; + + for (i = 0; i < PARTITIONS * COUNT; ++i) message[i] = 0; + + MPI_Request request; + MPI_Init_thread(&argc, &argv, MPI_THREAD_SERIALIZED, &provided); + if (provided < MPI_THREAD_SERIALIZED) MPI_Abort(MPI_COMM_WORLD , EXIT_FAILURE); MPI_Comm_rank(MPI_COMM_WORLD , &myrank); + + if (0 == myrank) { + + CHECK_RETVAL(MPI_Psend_init(message, partitions, COUNT, MPI_INT, dest, tag, MPI_COMM_WORLD , MPI_INFO_NULL , &request)); + CHECK_RETVAL(MPI_Start(&request)); + + /* Fill the sending partitions and mark as ready as each is filled */ + for (i = 0; i < partitions; ++i) { + for (j = 0; j < COUNT; ++j) message[j+(COUNT*i)] = j+(COUNT*i); + CHECK_RETVAL(MPI_Pready(i, request)); + } + + CHECK_RETVAL(MPI_Wait(&request, MPI_STATUS_IGNORE)); + + MPI_Request_free(&request); + + } + else if (1 == myrank) { + CHECK_RETVAL(MPI_Precv_init(message, partitions, COUNT, MPI_INT, source, tag, MPI_COMM_WORLD , MPI_INFO_NULL , &request)); + CHECK_RETVAL(MPI_Start(&request)); + CHECK_RETVAL(MPI_Wait(&request, MPI_STATUS_IGNORE)); + + MPI_Request_free(&request); + + /* all partitions received; check contents */ + for (i = 0; i < PARTITIONS*COUNT; ++i) { + if (message[i] != i) { + fprintf(stderr, "ERROR: Contents received do not match contents sent (expected %d, found %d).\n",i,message[i]); + MPI_Abort(MPI_COMM_WORLD, 1); + } + } + } + + MPI_Barrier(MPI_COMM_WORLD); + if (0 == myrank) {TEST_RAN_TO_COMPLETION();} + + MPI_Finalize(); + + return 0; +} + diff --git a/partitioned-communication/test_example2.c b/partitioned-communication/test_example2.c new file mode 100644 index 0000000..fd706f0 --- /dev/null +++ b/partitioned-communication/test_example2.c @@ -0,0 +1,100 @@ +// Copyright 2024 National Technology & Engineering Solutions of Sandia, LLC +// (NTESS). Under the terms of Contract DE-NA0003525 with NTESS, the U.S. +// Government retains certain rights in this software. + +/* + * This is a variation on example 4.2, p. 122-123, MPI Standard v. 4.1 + * + * Note that this example (like the original) uses the same congituous datatype on both + * sender and receiver. See test_example3x.c for other variations. + * + * Expected outcome: PASS + * + */ + +#include +#include "test_common.h" + +#define NUM_THREADS 8 +#define PARTITIONS 8 +#define PARTLENGTH 16 + +int main(int argc, char *argv[]) { + + int message[PARTITIONS*PARTLENGTH]; + int partitions = PARTITIONS; + int partlength = PARTLENGTH; + int count = 1, source = 0, dest = 1, tag = 1, flag = 0; + int i, j; + int my_thread_id; + int myrank; + int provided; + + omp_set_dynamic(0); // Disable dynamic teams + omp_set_num_threads(NUM_THREADS); + + for (i = 0; i < partitions * partlength; ++i) message[i] = 0; + + MPI_Request request; + MPI_Info info = MPI_INFO_NULL; + MPI_Datatype xfer_type; + MPI_Init_thread(&argc, &argv, MPI_THREAD_MULTIPLE, &provided); + if (provided < MPI_THREAD_MULTIPLE) MPI_Abort(MPI_COMM_WORLD , EXIT_FAILURE); + + MPI_Comm_rank(MPI_COMM_WORLD , &myrank); + MPI_Type_contiguous(partlength , MPI_INT, &xfer_type); + MPI_Type_commit(&xfer_type); + + if (0 == myrank) { + + MPI_Psend_init(message, partitions, count, xfer_type, dest, tag, MPI_COMM_WORLD, info, &request); + MPI_Start(&request); + +#pragma omp parallel for shared(request,message) private(j, my_thread_id) num_threads(NUM_THREADS) + for (i=0; i +#include "test_common.h" + +#define NUM_THREADS 8 +#define NUM_TASKS 64 +#define PARTITIONS NUM_TASKS +#define PARTLENGTH 16 +#define MESSAGE_LENGTH PARTITIONS*PARTLENGTH + +int main(int argc, char *argv[]) { + + int message[MESSAGE_LENGTH]; + int partitions = PARTITIONS; + int partlength = PARTLENGTH; + + int count = 1, source = 0, dest = 1, tag = 1, flag = 0; + int i, j; + int myrank; + int provided; + int my_thread_id; + + MPI_Request request; + MPI_Status status; + MPI_Info info = MPI_INFO_NULL; + MPI_Init_thread(&argc, &argv, MPI_THREAD_MULTIPLE, &provided); + if (provided < MPI_THREAD_MULTIPLE) MPI_Abort(MPI_COMM_WORLD , EXIT_FAILURE); + MPI_Comm_rank(MPI_COMM_WORLD , &myrank); + + if (0 == myrank) { + + for (i = 0; i < partitions * partlength; ++i) message[i] = 100; + + MPI_Psend_init(message, partitions, partlength, MPI_INT, dest, tag, MPI_COMM_WORLD, info, &request); + MPI_Start(&request); + +#pragma omp parallel shared(request,message) private(i, my_thread_id) num_threads(NUM_THREADS) + { +#pragma omp single + { + /* single thread creates 64 tasks to be executed by 8 threads */ + for (int task_num=0; task_num < NUM_TASKS; task_num++) { +#pragma omp task firstprivate(task_num) + { + my_thread_id = omp_get_thread_num(); + for (i=0; i < partlength; ++i) { + message[i + (task_num * partlength)] = i + (task_num * partlength); + } + MPI_Pready(task_num, request); + } /* end task */ + } /* end for */ + } /* end single */ + } /* end parallel */ + + // fprintf(stderr, "Sent: "); + // for (i = 0; i < MESSAGE_LENGTH; ++i) fprintf(stderr, "%d ", message[i]); + // fprintf(stderr, "\n"); + + while(!flag) { + MPI_Test(&request, &flag, MPI_STATUS_IGNORE); + } + + MPI_Request_free(&request); + + } else if (1 == myrank) { + for (i = 0; i < partitions * partlength; ++i) message[i] = 101; + + MPI_Precv_init(message, partitions, partlength, MPI_INT, source, tag, MPI_COMM_WORLD, info, &request); + MPI_Start(&request); + + while(!flag) { + MPI_Test(&request, &flag, MPI_STATUS_IGNORE); + } + + MPI_Request_free(&request); + + // fprintf(stderr, "Received: "); + // for (i = 0; i < MESSAGE_LENGTH; ++i) fprintf(stderr, "%d ", message[i]); + // fprintf(stderr, "\n"); + + /* all partitions received; check contents */ + for (i = 0; i < MESSAGE_LENGTH; ++i) { + if (message[i] != i) { + fprintf(stderr, "ERROR: Contents received do not match contents sent (expected %d, found %d).\n",i,message[i]); + MPI_Abort(MPI_COMM_WORLD, 1); + } + } + } + + MPI_Barrier(MPI_COMM_WORLD); + if (0 == myrank) {TEST_RAN_TO_COMPLETION();} + + MPI_Finalize(); + return 0; + +} diff --git a/partitioned-communication/test_example3b.c b/partitioned-communication/test_example3b.c new file mode 100644 index 0000000..7432081 --- /dev/null +++ b/partitioned-communication/test_example3b.c @@ -0,0 +1,110 @@ +// Copyright 2024 National Technology & Engineering Solutions of Sandia, LLC +// (NTESS). Under the terms of Contract DE-NA0003525 with NTESS, the U.S. +// Government retains certain rights in this software. + +/* + * Variation on Example 4.3, pp. 123-124 + * + * Original example: + * - Uses MPI_DOUBLE + * - Uses derived contiguous datatype on send side and MPI_DOUBLE on recv side + * - Has multiple partitions on send side and a single partition on recv side + * + * This version: + * - Uses MPI_INT + * - Does not use contiguous datatype on either side + * - Does have multiple partitions on send side and a single partition on recv side + * + * Expected outcome: PASS + * + */ + +#include +#include "test_common.h" + +#define NUM_THREADS 8 +#define NUM_TASKS 64 +#define PARTITIONS NUM_TASKS +#define PARTLENGTH 16 +#define MESSAGE_LENGTH PARTITIONS*PARTLENGTH + +int main(int argc, char *argv[]) { + + int message[MESSAGE_LENGTH]; + int send_partitions = PARTITIONS; + int send_partlength = PARTLENGTH; + int recv_partitions = 1; + int recv_partlength = MESSAGE_LENGTH; + + int count = 1, source = 0, dest = 1, tag = 1, flag = 0; + int i, j; + int myrank; + int provided; + int my_thread_id; + + MPI_Request request; + MPI_Status status; + MPI_Info info = MPI_INFO_NULL; + MPI_Init_thread(&argc, &argv, MPI_THREAD_MULTIPLE, &provided); + if (provided < MPI_THREAD_MULTIPLE) MPI_Abort(MPI_COMM_WORLD , EXIT_FAILURE); + MPI_Comm_rank(MPI_COMM_WORLD , &myrank); + + if (0 == myrank) { + + for (i = 0; i < send_partitions * send_partlength; ++i) message[i] = 100; + + MPI_Psend_init(message, send_partitions, send_partlength, MPI_INT, dest, tag, MPI_COMM_WORLD, info, &request); + MPI_Start(&request); + +#pragma omp parallel shared(request,message) private(i, my_thread_id) num_threads(NUM_THREADS) + { +#pragma omp single + { + /* single thread creates 64 tasks to be executed by 8 threads */ + for (int task_num=0; task_num < NUM_TASKS; task_num++) { +#pragma omp task firstprivate(task_num) + { + my_thread_id = omp_get_thread_num(); + for (i=0; i < send_partlength; ++i) { + message[i + (task_num * send_partlength)] = i + (task_num * send_partlength); + } + MPI_Pready(task_num, request); + } /* end task */ + } /* end for */ + } /* end single */ + } /* end parallel */ + + while(!flag) { + MPI_Test(&request, &flag, MPI_STATUS_IGNORE); + } + + MPI_Request_free(&request); + + } else if (1 == myrank) { + for (i = 0; i < recv_partitions * recv_partlength; ++i) message[i] = 101; + + MPI_Precv_init(message, recv_partitions, recv_partlength, MPI_INT, source, tag, MPI_COMM_WORLD, info, &request); + MPI_Start(&request); + + while(!flag) { + MPI_Test(&request, &flag, MPI_STATUS_IGNORE); + } + + MPI_Request_free(&request); + + /* all partitions received; check contents */ + for (i = 0; i < MESSAGE_LENGTH; ++i) { + if (message[i] != i) { + fprintf(stderr, "ERROR: Contents received do not match contents sent (expected %d, found %d).\n",i,message[i]); + MPI_Abort(MPI_COMM_WORLD, 1); + } + } + } + + MPI_Barrier(MPI_COMM_WORLD); + if (0 == myrank) {TEST_RAN_TO_COMPLETION();} + + MPI_Finalize(); + return 0; + +} diff --git a/partitioned-communication/test_example3c.c b/partitioned-communication/test_example3c.c new file mode 100644 index 0000000..640e48e --- /dev/null +++ b/partitioned-communication/test_example3c.c @@ -0,0 +1,114 @@ +// Copyright 2024 National Technology & Engineering Solutions of Sandia, LLC +// (NTESS). Under the terms of Contract DE-NA0003525 with NTESS, the U.S. +// Government retains certain rights in this software. + +/* + * Variation on Example 4.3, pp. 123-124 + * + * Original example: + * - Uses MPI_DOUBLE + * - Uses derived contiguous datatype on send side and MPI_DOUBLE on recv side + * - Has multiple partitions on send side and a single partition on recv side + * + * This version: + * - Uses MPI_INT + * + * See also test_datatypeX.c + * + * Expected outcome: PASS + * + */ + +#include +#include "test_common.h" + +#define NUM_THREADS 8 +#define NUM_TASKS 64 +#define PARTITIONS NUM_TASKS +#define PARTLENGTH 16 +#define MESSAGE_LENGTH PARTITIONS*PARTLENGTH + +int main(int argc, char *argv[]) { + + int message[MESSAGE_LENGTH]; + int send_partitions = PARTITIONS; + int send_partlength = PARTLENGTH; + int recv_partitions = 1; + int recv_partlength = MESSAGE_LENGTH; + + int count = 1, source = 0, dest = 1, tag = 1, flag = 0; + int i, j; + int myrank; + int provided; + int my_thread_id; + + MPI_Request request; + MPI_Status status; + MPI_Datatype send_type; + MPI_Info info = MPI_INFO_NULL; + MPI_Init_thread(&argc, &argv, MPI_THREAD_MULTIPLE, &provided); + if (provided < MPI_THREAD_MULTIPLE) MPI_Abort(MPI_COMM_WORLD , EXIT_FAILURE); + MPI_Comm_rank(MPI_COMM_WORLD , &myrank); + + /* Sender uses this datatype */ + MPI_Type_contiguous(send_partlength, MPI_INT, &send_type); + MPI_Type_commit(&send_type); + + if (0 == myrank) { + + for (i = 0; i < send_partitions * send_partlength; ++i) message[i] = 100; + + MPI_Psend_init(message, send_partitions, count, send_type, dest, tag, MPI_COMM_WORLD, info, &request); + MPI_Start(&request); + +#pragma omp parallel shared(request,message) private(i, my_thread_id) num_threads(NUM_THREADS) + { +#pragma omp single + { + /* single thread creates 64 tasks to be executed by 8 threads */ + for (int task_num=0; task_num < NUM_TASKS; task_num++) { +#pragma omp task firstprivate(task_num) + { + my_thread_id = omp_get_thread_num(); + for (i=0; i < send_partlength; ++i) { + message[i + (task_num * send_partlength)] = i + (task_num * send_partlength); + } + MPI_Pready(task_num, request); + } /* end task */ + } /* end for */ + } /* end single */ + } /* end parallel */ + + while(!flag) { + MPI_Test(&request, &flag, MPI_STATUS_IGNORE); + } + + MPI_Request_free(&request); + + } else if (1 == myrank) { + for (i = 0; i < recv_partitions * recv_partlength; ++i) message[i] = 101; + + MPI_Precv_init(message, recv_partitions, recv_partlength, MPI_INT, source, tag, MPI_COMM_WORLD, info, &request); + MPI_Start(&request); + + while(!flag) { + MPI_Test(&request, &flag, MPI_STATUS_IGNORE); + } + + MPI_Request_free(&request); + + /* all partitions received; check contents */ + for (i = 0; i < MESSAGE_LENGTH; ++i) { + if (message[i] != i) { + fprintf(stderr, "ERROR: Contents received do not match contents sent (expected %d, found %d).\n",i,message[i]); + MPI_Abort(MPI_COMM_WORLD, 1); + } + } + } + + MPI_Barrier(MPI_COMM_WORLD); + if (0 == myrank) {TEST_RAN_TO_COMPLETION();} + + MPI_Finalize(); + return 0; +} diff --git a/partitioned-communication/test_free0.c b/partitioned-communication/test_free0.c new file mode 100644 index 0000000..9b06de3 --- /dev/null +++ b/partitioned-communication/test_free0.c @@ -0,0 +1,90 @@ +// Copyright 2024 National Technology & Engineering Solutions of Sandia, LLC +// (NTESS). Under the terms of Contract DE-NA0003525 with NTESS, the U.S. +// Government retains certain rights in this software. + +/* + * P. 116, line 11: Freeing or canceling a partitioned communication request that is active (i.e., initialized and started) and not completed is erroneous. + * + * Expected outcome: Error message + * + */ + +#include "test_common.h" + +#define PARTITIONS 8 +#define COUNT 5 + +int main(int argc, char *argv[]) { + + int message[PARTITIONS*COUNT]; + + MPI_Count partitions = PARTITIONS; + + int source = 0, dest = 1, tag = 1, flag = 0; + int myrank, i, j; + int provided; + + for (i = 0; i < PARTITIONS * COUNT; ++i) message[i] = 0; + + MPI_Request request; + MPI_Init_thread(&argc, &argv, MPI_THREAD_SERIALIZED, &provided); + if (provided < MPI_THREAD_SERIALIZED) MPI_Abort(MPI_COMM_WORLD , EXIT_FAILURE); MPI_Comm_rank(MPI_COMM_WORLD , &myrank); + + if (0 == myrank) { + + fprintf(stderr, "Rank 0 initializing and starting Psend\n"); + MPI_Psend_init(message, partitions, COUNT, MPI_INT, dest, tag, MPI_COMM_WORLD , MPI_INFO_NULL , &request); + MPI_Start(&request); + + /* This should cause an error */ + fprintf(stderr, "Rank 0 freeing request (erroneous)\n"); + CHECK_RETVAL(MPI_Request_free(&request)); + + + /* Rest of code is the standard Psend/Precv example. If the MPI_Request_free is ignored and no error reported, + * then the remainder of the code may hang because the first Psend is never completed, and the destination is testing on it + */ + + fprintf(stderr, "Rank 0 starting second Psend\n"); + CHECK_RETVAL(MPI_Psend_init(message, partitions, COUNT, MPI_INT, dest, tag, MPI_COMM_WORLD , MPI_INFO_NULL , &request)); + CHECK_RETVAL(MPI_Start(&request)); + + /* Fill the sending partitions and mark as ready as each is filled */ + for (i = 0; i < partitions; ++i) { + for (j = 0; j < COUNT; ++j) message[j+(COUNT*i)] = j+(COUNT*i); + CHECK_RETVAL(MPI_Pready(i, request)); + } + + while (!flag) { + MPI_Test(&request, &flag, MPI_STATUS_IGNORE); + } + + MPI_Request_free(&request); + + } + else if (1 == myrank) { + MPI_Precv_init(message, partitions, COUNT, MPI_INT, source, tag, MPI_COMM_WORLD , MPI_INFO_NULL , &request); + MPI_Start(&request); + while (!flag) { + MPI_Test(&request, &flag, MPI_STATUS_IGNORE); + } + + MPI_Request_free(&request); + + /* all partitions received; check contents */ + for (i = 0; i < PARTITIONS*COUNT; ++i) { + if (message[i] != i) { + fprintf(stderr, "ERROR: Contents received do not match contents sent (expected %d, found %d).\n",i,message[i]); + MPI_Abort(MPI_COMM_WORLD, 1); + } + } + } + + MPI_Barrier(MPI_COMM_WORLD); + if (0 == myrank) {TEST_RAN_TO_COMPLETION();} + + MPI_Finalize(); + + return 0; +} + diff --git a/partitioned-communication/test_init0.c b/partitioned-communication/test_init0.c new file mode 100644 index 0000000..c0b3d45 --- /dev/null +++ b/partitioned-communication/test_init0.c @@ -0,0 +1,98 @@ +// Copyright 2024 National Technology & Engineering Solutions of Sandia, LLC +// (NTESS). Under the terms of Contract DE-NA0003525 with NTESS, the U.S. +// Government retains certain rights in this software. + +/* + * + * P. 117, line 13: PSEND_INIT can only match with partitioned communication initialization operations, therefore it is required to be matched with a corresponding MPI_PRECV_INIT. + * + * This test tries to match a PSEND_INIT with an IRECV. Because the PSEND_INIT should not match the + * IRECV, the code should timeout waiting + * + * Expected outcome: TIMEOUT + * + */ + +#include "test_common.h" + +#define PARTITIONS 1 +#define COUNT 64 + +int main(int argc, char *argv[]) { + + int message[PARTITIONS*COUNT]; + + MPI_Count partitions = PARTITIONS; + + int source = 0, dest = 1, tag = 1, flag = 0; + int myrank, i, j; + int provided; + + for (i = 0; i < PARTITIONS * COUNT; ++i) message[i] = 0; + + MPI_Request request; + MPI_Init_thread(&argc, &argv, MPI_THREAD_SERIALIZED, &provided); + if (provided < MPI_THREAD_SERIALIZED) MPI_Abort(MPI_COMM_WORLD , EXIT_FAILURE); MPI_Comm_rank(MPI_COMM_WORLD , &myrank); + + if (0 == myrank) { + + MPI_Psend_init(message, partitions, COUNT, MPI_INT, dest, tag, MPI_COMM_WORLD , MPI_INFO_NULL , &request); + MPI_Start(&request); + + /* Fill the sending partitions and mark as ready as each is filled */ + for (i = 0; i < partitions; ++i) { + for (j = 0; j < COUNT; ++j) message[j+(COUNT*i)] = j+(COUNT*i); + MPI_Pready(i, request); + } + + fprintf(stderr, "Sending message: \n"); + for (i=0; i < partitions * COUNT; ++i) { + fprintf(stderr, "%d, ", message[i]); + } + fprintf(stderr, "\n"); + + while (!flag) { + MPI_Test(&request, &flag, MPI_STATUS_IGNORE); + } + + MPI_Request_free(&request); + + } + else if (1 == myrank) { + + fprintf(stderr, "Received message: \n"); + for (i=0; i < partitions * COUNT; ++i) { + fprintf(stderr, "%d, ", message[i]); + } + fprintf(stderr, "\n"); + + /* This should not work. Test should time out */ + fprintf(stderr, "Rank 1 posting Irecv\n"); + CHECK_RETVAL(MPI_Irecv(message, COUNT, MPI_INT, source, tag, MPI_COMM_WORLD, &request)); + fprintf(stderr, "Rank 1 waiting\n"); + CHECK_RETVAL(MPI_Wait(&request, MPI_STATUS_IGNORE)); /* should time out here */ + + fprintf(stderr, "Received message: \n"); + for (i=0; i < partitions * COUNT; ++i) { + fprintf(stderr, "%d, ", message[i]); + } + fprintf(stderr, "\n"); + + /* all partitions received; check contents */ + for (i = 0; i < PARTITIONS*COUNT; ++i) { + if (message[i] != i) { + fprintf(stderr, "ERROR: Contents received do not match contents sent (expected %d, found %d).\n",i,message[i]); + MPI_Abort(MPI_COMM_WORLD, 1); + } + } + fprintf(stderr, "Rank 1: Contents verfied\n"); + } + + MPI_Barrier(MPI_COMM_WORLD); + if (0 == myrank) {TEST_RAN_TO_COMPLETION();} + + MPI_Finalize(); + + return 0; +} + diff --git a/partitioned-communication/test_init1.c b/partitioned-communication/test_init1.c new file mode 100644 index 0000000..2cf3401 --- /dev/null +++ b/partitioned-communication/test_init1.c @@ -0,0 +1,86 @@ +// Copyright 2024 National Technology & Engineering Solutions of Sandia, LLC +// (NTESS). Under the terms of Contract DE-NA0003525 with NTESS, the U.S. +// Government retains certain rights in this software. + +/* + * + * P. 118, line 20: PRECV_INIT can only match with partitioned communication initialization operations, therefore + * the MPI library is required to match MPI_PRECV_INIT calls only with a corresponding MPI_PSEND_INIT call. + * + * This test uses PRECV_INIT with an Isend; the latter should fail to match and the test timeout + * + * Expected outcome: TIMEOUT + * + */ + +#include "test_common.h" + +#define PARTITIONS 1 +#define COUNT 64 + +int main(int argc, char *argv[]) { + + int message[PARTITIONS*COUNT]; + + MPI_Count partitions = PARTITIONS; + + int source = 0, dest = 1, tag = 1, flag = 0; + int myrank, i, j; + int provided; + + for (i = 0; i < PARTITIONS * COUNT; ++i) message[i] = 0; + + MPI_Request request; + MPI_Init_thread(&argc, &argv, MPI_THREAD_SERIALIZED, &provided); + if (provided < MPI_THREAD_SERIALIZED) MPI_Abort(MPI_COMM_WORLD , EXIT_FAILURE); + MPI_Comm_rank(MPI_COMM_WORLD , &myrank); + + if (0 == myrank) { + + /* Fill message to be sent */ + for (i = 0; i < partitions; ++i) { + for (j = 0; j < COUNT; ++j) message[j+(COUNT*i)] = j+(COUNT*i); + } + + fprintf(stderr, "Rank 0 issuing Isend to Rank 1's corresponding MPI_Precv_init\n"); + MPI_Isend(message, COUNT, MPI_INT, dest, tag, MPI_COMM_WORLD, &request); + + fprintf(stderr, "Rank 0 waiting\n"); + MPI_Wait(&request, MPI_STATUS_IGNORE); + + fprintf(stderr, "Rank 0 done\n"); + + } + else if (1 == myrank) { + + fprintf(stderr, "Rank 1 initializing and starting Precv\n"); + CHECK_RETVAL(MPI_Precv_init(message, partitions, COUNT, MPI_INT, source, tag, MPI_COMM_WORLD, MPI_INFO_NULL, &request)); + CHECK_RETVAL(MPI_Start(&request)); + + fprintf(stderr, "Rank 1 testing for completion ... should timeout here if Precv did not match Isend\n"); + while (!flag) { + MPI_Test(&request, &flag, MPI_STATUS_IGNORE); + } + + MPI_Request_free(&request); + + fprintf(stderr, "Rank 1 received message\n"); + + /* all partitions received; check contents */ + for (i = 0; i < PARTITIONS*COUNT; ++i) { + if (message[i] != i) { + fprintf(stderr, "ERROR: Contents received do not match contents sent (expected %d, found %d).\n",i,message[i]); + MPI_Abort(MPI_COMM_WORLD, 1); + } + } + fprintf(stderr, "Rank 1: Contents verfied\n"); + } + + MPI_Barrier(MPI_COMM_WORLD); + if (0 == myrank) {TEST_RAN_TO_COMPLETION();} + + MPI_Finalize(); + + return 0; +} + diff --git a/partitioned-communication/test_init2.c b/partitioned-communication/test_init2.c new file mode 100644 index 0000000..9d86c7c --- /dev/null +++ b/partitioned-communication/test_init2.c @@ -0,0 +1,88 @@ +// Copyright 2024 National Technology & Engineering Solutions of Sandia, LLC +// (NTESS). Under the terms of Contract DE-NA0003525 with NTESS, the U.S. +// Government retains certain rights in this software. + +/* + * + * P. 118, line 20: PRECV_INIT can only match with partitioned communication initialization operations, therefore + * the MPI library is required to match MPI_PRECV_INIT calls only with a corresponding MPI_PSEND_INIT call. + * + * This version use persistent send + * + * Expected outcome: TIMEOUT + * + */ + +#include "test_common.h" + +#define PARTITIONS 1 +#define COUNT 64 + +int main(int argc, char *argv[]) { + + int message[PARTITIONS*COUNT]; + + MPI_Count partitions = PARTITIONS; + + int source = 0, dest = 1, tag = 1, flag = 0; + int myrank, i, j; + int provided; + + for (i = 0; i < PARTITIONS * COUNT; ++i) message[i] = 0; + + MPI_Request request; + MPI_Init_thread(&argc, &argv, MPI_THREAD_SERIALIZED, &provided); + if (provided < MPI_THREAD_SERIALIZED) MPI_Abort(MPI_COMM_WORLD , EXIT_FAILURE); MPI_Comm_rank(MPI_COMM_WORLD , &myrank); + + if (0 == myrank) { + + fprintf(stderr, "Rank 0 initializing persistent send\n"); + MPI_Send_init(message, COUNT, MPI_INT, dest, tag, MPI_COMM_WORLD, &request); + + /* Fill message to be sent */ + for (i = 0; i < partitions; ++i) { + for (j = 0; j < COUNT; ++j) message[j+(COUNT*i)] = j+(COUNT*i); + } + + fprintf(stderr, "Rank 0 starting persistent send\n"); + MPI_Start(&request); + + fprintf(stderr, "Rank 0 waiting\n"); + MPI_Wait(&request, MPI_STATUS_IGNORE); + + fprintf(stderr, "Rank 0 done\n"); + + } + else if (1 == myrank) { + + fprintf(stderr, "Rank 1 initializing and starting Precv\n"); + MPI_Precv_init(message, partitions, COUNT, MPI_INT, source, tag, MPI_COMM_WORLD, MPI_INFO_NULL, &request); + MPI_Start(&request); + + fprintf(stderr, "Rank 1 testing for completion ... should timeout here if Precv did not match persistent send\n"); + while (!flag) { + MPI_Test(&request, &flag, MPI_STATUS_IGNORE); + } + + MPI_Request_free(&request); + + fprintf(stderr, "Rank 1 received message\n"); + + /* all partitions received; check contents */ + for (i = 0; i < PARTITIONS*COUNT; ++i) { + if (message[i] != i) { + fprintf(stderr, "ERROR: Contents received do not match contents sent (expected %d, found %d).\n",i,message[i]); + MPI_Abort(MPI_COMM_WORLD, 1); + } + } + fprintf(stderr, "Rank 1: Contents verfied\n"); + } + + MPI_Barrier(MPI_COMM_WORLD); + if (0 == myrank) {TEST_RAN_TO_COMPLETION();} + + MPI_Finalize (); + + return 0; +} + diff --git a/partitioned-communication/test_local0.c b/partitioned-communication/test_local0.c new file mode 100644 index 0000000..644caa6 --- /dev/null +++ b/partitioned-communication/test_local0.c @@ -0,0 +1,86 @@ +// Copyright 2024 National Technology & Engineering Solutions of Sandia, LLC +// (NTESS). Under the terms of Contract DE-NA0003525 with NTESS, the U.S. +// Government retains certain rights in this software. + +/* + * P. 114, line 11: Part comm initialization functions are local. + * P. 117, line 14-15: Partitioned communication initialization calls are local. + * + * Therefore, the sender should be able to get to test/wait before the receiver calls PRECV_INIT, and + * the receiver should be able to get to test/wait before the sender calls PSEND_INIT + * + * Expected outcome: PASS + * + */ + +#include "test_common.h" + +#define PARTITIONS 8 +#define COUNT 5 + +int main(int argc, char *argv[]) { + + int message[PARTITIONS*COUNT]; + + MPI_Count partitions = PARTITIONS; + + int source = 0, dest = 1, tag = 1, flag = 0; + int myrank, i, j; + int provided; + + for (i = 0; i < PARTITIONS * COUNT; ++i) message[i] = 0; + + MPI_Request request; + MPI_Init_thread(&argc, &argv, MPI_THREAD_SERIALIZED, &provided); + if (provided < MPI_THREAD_SERIALIZED) MPI_Abort(MPI_COMM_WORLD , EXIT_FAILURE); MPI_Comm_rank(MPI_COMM_WORLD , &myrank); + + if (0 == myrank) { + + MPI_Psend_init(message, partitions, COUNT, MPI_INT, dest, tag, MPI_COMM_WORLD , MPI_INFO_NULL , &request); + MPI_Start(&request); + + /* Fill the sending partitions and mark as ready as each is filled */ + for (i = 0; i < partitions; ++i) { + for (j = 0; j < COUNT; ++j) message[j+(COUNT*i)] = j+(COUNT*i); + MPI_Pready(i, request); + } + + /* receiver does not call PRECV_INIT until after this barrier */ + /* putting barrier after test causes deadlock */ + MPI_Barrier(MPI_COMM_WORLD); + + while (!flag) { + MPI_Test(&request, &flag, MPI_STATUS_IGNORE); + } + + MPI_Request_free(&request); + + } + else if (1 == myrank) { + MPI_Barrier(MPI_COMM_WORLD); + + MPI_Precv_init(message, partitions, COUNT, MPI_INT, source, tag, MPI_COMM_WORLD , MPI_INFO_NULL , &request); + MPI_Start(&request); + while (!flag) { + MPI_Test(&request, &flag, MPI_STATUS_IGNORE); + } + + MPI_Request_free(&request); + + /* all partitions received; check contents */ + for (i = 0; i < PARTITIONS*COUNT; ++i) { + if (message[i] != i) { + fprintf(stderr, "ERROR: Contents received do not match contents sent (expected %d, found %d).\n",i,message[i]); + MPI_Abort(MPI_COMM_WORLD, 1); + } + } + } + + MPI_Barrier(MPI_COMM_WORLD); + if (1 == myrank) {TEST_RAN_TO_COMPLETION();} + + MPI_Finalize(); + + return 0; +} + diff --git a/partitioned-communication/test_local1.c b/partitioned-communication/test_local1.c new file mode 100644 index 0000000..9dc5c28 --- /dev/null +++ b/partitioned-communication/test_local1.c @@ -0,0 +1,85 @@ +// Copyright 2024 National Technology & Engineering Solutions of Sandia, LLC +// (NTESS). Under the terms of Contract DE-NA0003525 with NTESS, the U.S. +// Government retains certain rights in this software. + +/* + * P. 114, line 11: Part comm initialization functions are local. + * P. 117, line 14-15: Partitioned communication initialization calls are local. + * + * Therefore, the sender should be able to get to test/wait before the receiver calls PRECV_INIT, and + * the receiver should be able to get to test/wait before the sender calls PSEND_INIT + * + * Expected outcome: PASS + * + */ + +#include "test_common.h" + +#define PARTITIONS 8 +#define COUNT 5 + +int main(int argc, char *argv[]) { + + int message[PARTITIONS*COUNT]; + + MPI_Count partitions = PARTITIONS; + + int source = 0, dest = 1, tag = 1, flag = 0; + int myrank, i, j; + int provided; + + for (i = 0; i < PARTITIONS * COUNT; ++i) message[i] = 0; + + MPI_Request request; + MPI_Init_thread(&argc, &argv, MPI_THREAD_SERIALIZED, &provided); + if (provided < MPI_THREAD_SERIALIZED) MPI_Abort(MPI_COMM_WORLD , EXIT_FAILURE); MPI_Comm_rank(MPI_COMM_WORLD , &myrank); + + if (0 == myrank) { + + MPI_Barrier(MPI_COMM_WORLD); + + MPI_Psend_init(message, partitions, COUNT, MPI_INT, dest, tag, MPI_COMM_WORLD , MPI_INFO_NULL , &request); + MPI_Start(&request); + + /* Fill the sending partitions and mark as ready as each is filled */ + for (i = 0; i < partitions; ++i) { + for (j = 0; j < COUNT; ++j) message[j+(COUNT*i)] = j+(COUNT*i); + MPI_Pready(i, request); + } + + while (!flag) { + MPI_Test(&request, &flag, MPI_STATUS_IGNORE); + } + + MPI_Request_free(&request); + + } + else if (1 == myrank) { + + + MPI_Precv_init(message, partitions, COUNT, MPI_INT, source, tag, MPI_COMM_WORLD , MPI_INFO_NULL , &request); + MPI_Start(&request); + MPI_Barrier(MPI_COMM_WORLD); /* barrier after test causes deadlock */ + while (!flag) { + MPI_Test(&request, &flag, MPI_STATUS_IGNORE); + } + + MPI_Request_free(&request); + + /* all partitions received; check contents */ + for (i = 0; i < PARTITIONS*COUNT; ++i) { + if (message[i] != i) { + fprintf(stderr, "ERROR: Contents received do not match contents sent (expected %d, found %d).\n",i,message[i]); + MPI_Abort(MPI_COMM_WORLD, 1); + } + } + } + + MPI_Barrier(MPI_COMM_WORLD); + if (0 == myrank) {TEST_RAN_TO_COMPLETION();} + + MPI_Finalize(); + + return 0; +} + diff --git a/partitioned-communication/test_numparts0.c b/partitioned-communication/test_numparts0.c new file mode 100644 index 0000000..55dc524 --- /dev/null +++ b/partitioned-communication/test_numparts0.c @@ -0,0 +1,82 @@ +// Copyright 2024 National Technology & Engineering Solutions of Sandia, LLC +// (NTESS). Under the terms of Contract DE-NA0003525 with NTESS, the U.S. +// Government retains certain rights in this software. + +/* + * + * Test 4: P. 114, line 12-13: The number of user-visible partitions on the send and receiver side may differ. + * + * This test has twice as many send partitions as recv partitions + * + * Expected outcome: PASS + * + */ + +#include "test_common.h" + +#define PARTITIONS 8 +#define COUNT 8 + +int main(int argc, char *argv[]) { + + int message[PARTITIONS*COUNT]; + + MPI_Count send_partitions = PARTITIONS * 2; + MPI_Count send_count = (int)COUNT/2; + MPI_Count recv_partitions = PARTITIONS; + MPI_Count recv_count = COUNT; + + int source = 0, dest = 1, tag = 1, flag = 0; + int myrank, i, j; + int provided; + + for (i = 0; i < PARTITIONS * COUNT; ++i) message[i] = 0; + + MPI_Request request; + MPI_Init_thread(&argc, &argv, MPI_THREAD_SERIALIZED, &provided); + if (provided < MPI_THREAD_SERIALIZED) MPI_Abort(MPI_COMM_WORLD , EXIT_FAILURE); MPI_Comm_rank(MPI_COMM_WORLD , &myrank); + + if (0 == myrank) { + + MPI_Psend_init(message, send_partitions, send_count, MPI_INT, dest, tag, MPI_COMM_WORLD , MPI_INFO_NULL , &request); + MPI_Start(&request); + + /* Fill the sending partitions and mark as ready as each is filled */ + for (i = 0; i < send_partitions; ++i) { + for (j = 0; j < send_count; ++j) message[j+(send_count*i)] = j+(send_count*i); + MPI_Pready(i, request); + } + + while (!flag) { + MPI_Test(&request, &flag, MPI_STATUS_IGNORE); + } + + MPI_Request_free(&request); + + } + else if (1 == myrank) { + MPI_Precv_init(message, recv_partitions, recv_count, MPI_INT, source, tag, MPI_COMM_WORLD , MPI_INFO_NULL , &request); + MPI_Start(&request); + while (!flag) { + MPI_Test(&request, &flag, MPI_STATUS_IGNORE); + } + + MPI_Request_free(&request); + + /* all partitions received; check contents */ + for (i = 0; i < recv_partitions*recv_count; ++i) { + if (message[i] != i) { + fprintf(stderr, "ERROR: Contents received do not match contents sent (expected %d, found %d).\n",i,message[i]); + MPI_Abort(MPI_COMM_WORLD, 1); + } + } + } + + MPI_Barrier(MPI_COMM_WORLD); + if (0 == myrank) {TEST_RAN_TO_COMPLETION();} + + MPI_Finalize(); + + return 0; +} + diff --git a/partitioned-communication/test_numparts1.c b/partitioned-communication/test_numparts1.c new file mode 100644 index 0000000..605cf38 --- /dev/null +++ b/partitioned-communication/test_numparts1.c @@ -0,0 +1,82 @@ +// Copyright 2024 National Technology & Engineering Solutions of Sandia, LLC +// (NTESS). Under the terms of Contract DE-NA0003525 with NTESS, the U.S. +// Government retains certain rights in this software. + +/* + * + * Test 4: P. 114, line 12-13: The number of user-visible partitions on the send and receiver side may differ. + * + * This test has twice as many recv partitions as send partitions + * + * Expected outcome: PASS + * + */ + +#include "test_common.h" + +#define PARTITIONS 8 +#define COUNT 8 + +int main(int argc, char *argv[]) { + + int message[PARTITIONS*COUNT]; + + MPI_Count send_partitions = PARTITIONS; + MPI_Count send_count = COUNT; + MPI_Count recv_partitions = PARTITIONS * 2; + MPI_Count recv_count = (int)COUNT/2; + + int source = 0, dest = 1, tag = 1, flag = 0; + int myrank, i, j; + int provided; + + for (i = 0; i < PARTITIONS * COUNT; ++i) message[i] = 0; + + MPI_Request request; + MPI_Init_thread(&argc, &argv, MPI_THREAD_SERIALIZED, &provided); + if (provided < MPI_THREAD_SERIALIZED) MPI_Abort(MPI_COMM_WORLD , EXIT_FAILURE); MPI_Comm_rank(MPI_COMM_WORLD , &myrank); + + if (0 == myrank) { + + MPI_Psend_init(message, send_partitions, send_count, MPI_INT, dest, tag, MPI_COMM_WORLD , MPI_INFO_NULL , &request); + MPI_Start(&request); + + /* Fill the sending partitions and mark as ready as each is filled */ + for (i = 0; i < send_partitions; ++i) { + for (j = 0; j < send_count; ++j) message[j+(send_count*i)] = j+(send_count*i); + MPI_Pready(i, request); + } + + while (!flag) { + MPI_Test(&request, &flag, MPI_STATUS_IGNORE); + } + + MPI_Request_free(&request); + + } + else if (1 == myrank) { + MPI_Precv_init(message, recv_partitions, recv_count, MPI_INT, source, tag, MPI_COMM_WORLD , MPI_INFO_NULL , &request); + MPI_Start(&request); + while (!flag) { + MPI_Test(&request, &flag, MPI_STATUS_IGNORE); + } + + MPI_Request_free(&request); + + /* all partitions received; check contents */ + for (i = 0; i < recv_partitions*recv_count; ++i) { + if (message[i] != i) { + fprintf(stderr, "ERROR: Contents received do not match contents sent (expected %d, found %d).\n",i,message[i]); + MPI_Abort(MPI_COMM_WORLD, 1); + } + } + } + + MPI_Barrier(MPI_COMM_WORLD); + if (0 == myrank) {TEST_RAN_TO_COMPLETION();} + + MPI_Finalize(); + + return 0; +} + diff --git a/partitioned-communication/test_order0.c b/partitioned-communication/test_order0.c new file mode 100644 index 0000000..d140e09 --- /dev/null +++ b/partitioned-communication/test_order0.c @@ -0,0 +1,116 @@ +// Copyright 2024 National Technology & Engineering Solutions of Sandia, LLC +// (NTESS). Under the terms of Contract DE-NA0003525 with NTESS, the U.S. +// Government retains certain rights in this software. + +/* + * P. 113, line 15: Part comm operations are matched based on the order in which the local initialization calls are performed. + * P. 117, line 10: In the event that the communicator, tag, and source do not uniquely identify a message, the order + * in which partitioned communication initialization calls are made is the order in which they will eventually match. + * P. 118, line 24: If communicator, tag, and source are not enough to match, order is used. + * + * Expected outcome: PASS + * + */ + +#include "test_common.h" + +#define PARTITIONS 8 +#define COUNT 5 + +int main(int argc, char *argv[]) { + + int message0[PARTITIONS*COUNT]; + int message1[PARTITIONS*COUNT]; + + MPI_Count partitions = PARTITIONS; + + int source = 0, dest = 1, tag = 1, flag0 = 0, flag1 = 0; + int myrank, i, j; + int provided; + + for (i = 0; i < PARTITIONS * COUNT; ++i) message0[i] = 0; + for (i = 0; i < PARTITIONS * COUNT; ++i) message1[i] = 0; + + MPI_Request request0, request1; + MPI_Init_thread(&argc, &argv, MPI_THREAD_SERIALIZED, &provided); + if (provided < MPI_THREAD_SERIALIZED) MPI_Abort(MPI_COMM_WORLD , EXIT_FAILURE); MPI_Comm_rank(MPI_COMM_WORLD , &myrank); + + if (0 == myrank) { + + /* init in order 0, 1 */ + MPI_Psend_init(message0, partitions, COUNT, MPI_INT, dest, tag, MPI_COMM_WORLD , MPI_INFO_NULL , &request0); + MPI_Psend_init(message1, partitions, COUNT, MPI_INT, dest, tag, MPI_COMM_WORLD , MPI_INFO_NULL , &request1); + + /* start both */ + MPI_Start(&request0); + MPI_Start(&request1); + + /* fill 0; should fill 1 on receiver */ + for (i = 0; i < partitions; ++i) { + for (j = 0; j < COUNT; ++j) message0[j+(COUNT*i)] = 10*(j+(COUNT*i)); + MPI_Pready(i, request0); + } + + while (!flag0) { + MPI_Test(&request0, &flag0, MPI_STATUS_IGNORE); + } + + /* fill 1; should fill 0 on receiver */ + for (i = 0; i < partitions; ++i) { + for (j = 0; j < COUNT; ++j) message1[j+(COUNT*i)] = j+(COUNT*i); + MPI_Pready(i, request1); + } + + while (!flag1) { + MPI_Test(&request1, &flag1, MPI_STATUS_IGNORE); + } + + MPI_Request_free(&request0); + MPI_Request_free(&request1); + + } + else if (1 == myrank) { + /* init in order 1, 0 */ + MPI_Precv_init(message1, partitions, COUNT, MPI_INT, source, tag, MPI_COMM_WORLD , MPI_INFO_NULL , &request1); + MPI_Precv_init(message0, partitions, COUNT, MPI_INT, source, tag, MPI_COMM_WORLD , MPI_INFO_NULL , &request0); + + MPI_Start(&request0); + MPI_Start(&request1); + + while (!flag0 && !flag1) { + MPI_Test(&request0, &flag0, MPI_STATUS_IGNORE); + MPI_Test(&request1, &flag1, MPI_STATUS_IGNORE); + } + + MPI_Request_free(&request0); + MPI_Request_free(&request1); + + /* all partitions received; check contents */ + //fprintf(stderr, "Message 0: "); + for (i = 0; i < PARTITIONS*COUNT; ++i) { + //fprintf(stderr, "%d, ", message0[i]); + if (message0[i] != i) { + fprintf(stderr, "ERROR: Contents received in message buffer 0 do not match contents sent (expected %d, found %d).\n",i,message0[i]); + MPI_Abort(MPI_COMM_WORLD, 0); + } + } + //fprintf(stderr, "\n"); + //fprintf(stderr, "Message 1: "); + for (i = 0; i < PARTITIONS*COUNT; ++i) { + //fprintf(stderr, "%d, ", message1[i]); + if (message1[i] != i*10) { + fprintf(stderr, "ERROR: Contents received in message buffer 1 do not match contents sent (expected %d, found %d).\n",i,message1[i]); + MPI_Abort(MPI_COMM_WORLD, 1); + } + } + //fprintf(stderr, "\n"); + } + + MPI_Barrier(MPI_COMM_WORLD); + if (0 == myrank) {TEST_RAN_TO_COMPLETION();} + + MPI_Finalize(); + + return 0; +} + diff --git a/partitioned-communication/test_parrived0.c b/partitioned-communication/test_parrived0.c new file mode 100644 index 0000000..664e267 --- /dev/null +++ b/partitioned-communication/test_parrived0.c @@ -0,0 +1,92 @@ +// Copyright 2024 National Technology & Engineering Solutions of Sandia, LLC +// (NTESS). Under the terms of Contract DE-NA0003525 with NTESS, the U.S. +// Government retains certain rights in this software. + +/* + * P. 120: definition of PARRIVED + * + * This test simply uses PARRIVED and confirms the data in the partition is as expected + * + * Expected outcome: PASS + * + */ + +#include "test_common.h" + +#define PARTITIONS 8 +#define COUNT 5 + +int main(int argc, char *argv[]) { + + int message[PARTITIONS*COUNT]; + + MPI_Count partitions = PARTITIONS; + int part_of_interest = 3; + + int source = 0, dest = 1, tag = 1, flag = 0; + int myrank, i, j; + int provided; + + for (i = 0; i < PARTITIONS * COUNT; ++i) message[i] = 0; + + MPI_Request request; + MPI_Init_thread(&argc, &argv, MPI_THREAD_SERIALIZED, &provided); + if (provided < MPI_THREAD_SERIALIZED) MPI_Abort(MPI_COMM_WORLD , EXIT_FAILURE); MPI_Comm_rank(MPI_COMM_WORLD , &myrank); + + if (0 == myrank) { + + CHECK_RETVAL(MPI_Psend_init(message, partitions, COUNT, MPI_INT, dest, tag, MPI_COMM_WORLD , MPI_INFO_NULL , &request)); + CHECK_RETVAL(MPI_Start(&request)); + + /* Fill the sending partitions and mark as ready as each is filled */ + for (i = 0; i < partitions; ++i) { + for (j = 0; j < COUNT; ++j) message[j+(COUNT*i)] = j+(COUNT*i); + CHECK_RETVAL(MPI_Pready(i, request)); + } + + while (!flag) { + CHECK_RETVAL(MPI_Test(&request, &flag, MPI_STATUS_IGNORE)); + } + + MPI_Request_free(&request); + + } + else if (1 == myrank) { + CHECK_RETVAL(MPI_Precv_init(message, partitions, COUNT, MPI_INT, source, tag, MPI_COMM_WORLD , MPI_INFO_NULL , &request)); + CHECK_RETVAL(MPI_Start(&request)); + + /* loop until the partition of interest has arrived */ + int try = 0; + while (!flag) { + CHECK_RETVAL(MPI_Parrived(request, part_of_interest, &flag)); + ++try; + } + + /* acess the partition data */ + fprintf(stderr, "Number of PARRIVED calls: %d\n", try); + for (i=0; i < COUNT; ++i) { + fprintf(stderr,"%d\n", message[(part_of_interest*COUNT)+i]); + } + + /* continue as usual */ + MPI_Wait(&request, MPI_STATUS_IGNORE); + + MPI_Request_free(&request); + + /* all partitions received; check contents */ + for (i = 0; i < PARTITIONS*COUNT; ++i) { + if (message[i] != i) { + fprintf(stderr, "ERROR: Contents received do not match contents sent (expected %d, found %d).\n",i,message[i]); + MPI_Abort(MPI_COMM_WORLD, 1); + } + } + } + + MPI_Barrier(MPI_COMM_WORLD); + if (0 == myrank) {TEST_RAN_TO_COMPLETION();} + + MPI_Finalize(); + + return 0; +} + diff --git a/partitioned-communication/test_parrived1.c b/partitioned-communication/test_parrived1.c new file mode 100644 index 0000000..300618a --- /dev/null +++ b/partitioned-communication/test_parrived1.c @@ -0,0 +1,57 @@ +// Copyright 2024 National Technology & Engineering Solutions of Sandia, LLC +// (NTESS). Under the terms of Contract DE-NA0003525 with NTESS, the U.S. +// Government retains certain rights in this software. + +/* + * P. 121, line 10: PARRIVED can be called on a null or inactive request; in this case, flag = true + * + * Expected outcome: PASS + * + */ + +#include +#include "test_common.h" + +#define PARTITIONS 8 +#define COUNT 5 + +int main(int argc, char *argv[]) { + + int message[PARTITIONS*COUNT]; + + MPI_Count partitions = PARTITIONS; + int part_of_interest = 3; + + int source = 0, dest = 1, tag = 1, flag = 0; + int myrank, i, j; + int provided; + + for (i = 0; i < PARTITIONS * COUNT; ++i) message[i] = 0; + + MPI_Request request; + MPI_Init_thread(&argc, &argv, MPI_THREAD_SERIALIZED, &provided); + if (provided < MPI_THREAD_SERIALIZED) MPI_Abort(MPI_COMM_WORLD , EXIT_FAILURE); MPI_Comm_rank(MPI_COMM_WORLD , &myrank); + + if (0 == myrank) { + + sleep(10); + + } + else if (1 == myrank) { + + /* request never used; confirm parrived sets flag to true */ + if (!flag) fprintf(stderr, "Flag is false\n"); + while (!flag) { + CHECK_RETVAL(MPI_Parrived(request, 0, &flag)); + } + fprintf(stderr, "Flag is true\n"); + } + + MPI_Barrier(MPI_COMM_WORLD); + if (0 == myrank) {TEST_RAN_TO_COMPLETION();} + + MPI_Finalize(); + + return 0; +} + diff --git a/partitioned-communication/test_parrived2.c b/partitioned-communication/test_parrived2.c new file mode 100644 index 0000000..0b2ff46 --- /dev/null +++ b/partitioned-communication/test_parrived2.c @@ -0,0 +1,74 @@ +/* + * P. 121, line 11: Calling MPI_PARRIVED on a request that does not correspond to a partitioned receive operation + * is erroneous. + * + * This test uses PARRIVED on a request corresponding to an Irecv + * + * Expected outcome: some sort of error + * + */ + +#include "test_common.h" + +#define COUNT 32 + +int main(int argc, char *argv[]) { + + int message[COUNT]; + + int source = 0, dest = 1, tag = 1, flag = 0; + int myrank, i, j; + int provided; + + for (i = 0; i < COUNT; ++i) message[i] = 0; + + MPI_Request request; + MPI_Init_thread(&argc, &argv, MPI_THREAD_SERIALIZED, &provided); + if (provided < MPI_THREAD_SERIALIZED) MPI_Abort(MPI_COMM_WORLD , EXIT_FAILURE); + MPI_Comm_rank(MPI_COMM_WORLD , &myrank); + + if (0 == myrank) { + + for (i = 0; i < COUNT; ++i) message[i] = i; + + MPI_Isend(message, COUNT, MPI_INT, dest, tag, MPI_COMM_WORLD, &request); + + while (!flag) { + CHECK_RETVAL(MPI_Test(&request, &flag, MPI_STATUS_IGNORE)); + } + + //MPI_Request_free(&request); + + } + else if (1 == myrank) { + + MPI_Irecv(message, COUNT, MPI_INT, source, tag, MPI_COMM_WORLD, &request); + + /* this is erroneous */ + MPI_Parrived(request, 0, &flag); + + /* continue as usual */ + MPI_Wait(&request, MPI_STATUS_IGNORE); + + for (i=0; i < COUNT; ++i) { + fprintf(stderr,"%d ", message[i]); + } + fprintf(stderr, "\n"); + + /* all partitions received; check contents */ + for (i = 0; i < COUNT; ++i) { + if (message[i] != i) { + fprintf(stderr, "ERROR: Contents received do not match contents sent (expected %d, found %d).\n",i,message[i]); + MPI_Abort(MPI_COMM_WORLD, 1); + } + } + } + + MPI_Barrier(MPI_COMM_WORLD); + if (0 == myrank) {TEST_RAN_TO_COMPLETION();} + + MPI_Finalize(); + + return 0; +} + diff --git a/partitioned-communication/test_partitions0.c b/partitioned-communication/test_partitions0.c new file mode 100644 index 0000000..fc7457d --- /dev/null +++ b/partitioned-communication/test_partitions0.c @@ -0,0 +1,80 @@ +// Copyright 2024 National Technology & Engineering Solutions of Sandia, LLC +// (NTESS). Under the terms of Contract DE-NA0003525 with NTESS, the U.S. +// Government retains certain rights in this software. + +/* + * Initializes using an invalid number of partitions: Psend_init with 0 partitions. + * + * P. 114, line 13-14: Valid partitioned communication operations must have one or more partitions specified. + * P. 117, line 15-16: It is erroneous to provide a partitions value <= 0. + * P. 118, line 28: It is erroneous to provide a partitions value <= 0. + * + * Expected outcome: Error message + */ + +#include "test_common.h" + +#define PARTITIONS 8 +#define COUNT 5 + +int main(int argc, char *argv[]) { + + int message[PARTITIONS*COUNT]; + + MPI_Count send_partitions = 0; /* Bad number of send partitions */ + MPI_Count recv_partitions = PARTITIONS; + + int source = 0, dest = 1, tag = 1, flag = 0; + int myrank, i, j; + int provided; + + for (i = 0; i < PARTITIONS * COUNT; ++i) message[i] = 0; + + MPI_Request request; + MPI_Init_thread(&argc, &argv, MPI_THREAD_SERIALIZED, &provided); + if (provided < MPI_THREAD_SERIALIZED) MPI_Abort(MPI_COMM_WORLD , EXIT_FAILURE); MPI_Comm_rank(MPI_COMM_WORLD , &myrank); + + if (0 == myrank) { + + /* Error should occur with this call */ + CHECK_RETVAL(MPI_Psend_init(message, send_partitions, COUNT, MPI_INT, dest, tag, MPI_COMM_WORLD , MPI_INFO_NULL , &request)); + CHECK_RETVAL(MPI_Start(&request)); + + for (i = 0; i < send_partitions; ++i) { + for (j = 0; j < COUNT; ++j) message[j+(COUNT*i)] = j+(COUNT*i); + CHECK_RETVAL(MPI_Pready(i, request)); + } + + while (!flag) { + CHECK_RETVAL(MPI_Test(&request, &flag, MPI_STATUS_IGNORE)); + } + + MPI_Request_free(&request); + + } + else if (1 == myrank) { + MPI_Precv_init(message, recv_partitions, COUNT, MPI_INT, source, tag, MPI_COMM_WORLD , MPI_INFO_NULL , &request); + MPI_Start(&request); + while (!flag) { + MPI_Test(&request, &flag, MPI_STATUS_IGNORE); + } + + MPI_Request_free(&request); + + /* all partitions received; check contents */ + for (i = 0; i < PARTITIONS*COUNT; ++i) { + if (message[i] != i) { + fprintf(stderr, "ERROR: Contents received do not match contents sent (expected %d, found %d).\n",i,message[i]); + MPI_Abort(MPI_COMM_WORLD, 1); + } + } + } + + MPI_Barrier(MPI_COMM_WORLD); + if (0 == myrank) {TEST_RAN_TO_COMPLETION();} + + MPI_Finalize(); + + return 0; +} + diff --git a/partitioned-communication/test_partitions1.c b/partitioned-communication/test_partitions1.c new file mode 100644 index 0000000..ef66120 --- /dev/null +++ b/partitioned-communication/test_partitions1.c @@ -0,0 +1,80 @@ +// Copyright 2024 National Technology & Engineering Solutions of Sandia, LLC +// (NTESS). Under the terms of Contract DE-NA0003525 with NTESS, the U.S. +// Government retains certain rights in this software. + +/* + * Initializes using an invalid number of partitions: Psend_init with negative partitions. + * + * P. 114, line 13-14: Valid partitioned communication operations must have one or more partitions specified. + * P. 117, line 15-16: It is erroneous to provide a partitions value <= 0. + * P. 118, line 28: It is erroneous to provide a partitions value <= 0. + * + * Expected outcome: Error message + */ + +#include "test_common.h" + +#define PARTITIONS 8 +#define COUNT 5 + +int main(int argc, char *argv[]) { + + int message[PARTITIONS*COUNT]; + + MPI_Count send_partitions = -8; /* Bad number of partitions; MPI_Count is a signed int */ + MPI_Count recv_partitions = PARTITIONS; + + int source = 0, dest = 1, tag = 1, flag = 0; + int myrank, i, j; + int provided; + + for (i = 0; i < PARTITIONS * COUNT; ++i) message[i] = 0; + + MPI_Request request; + MPI_Init_thread(&argc, &argv, MPI_THREAD_SERIALIZED, &provided); + if (provided < MPI_THREAD_SERIALIZED) MPI_Abort(MPI_COMM_WORLD , EXIT_FAILURE); MPI_Comm_rank(MPI_COMM_WORLD , &myrank); + + if (0 == myrank) { + + /* Error should occur with this call */ + CHECK_RETVAL(MPI_Psend_init(message, send_partitions, COUNT, MPI_INT, dest, tag, MPI_COMM_WORLD , MPI_INFO_NULL , &request)); + CHECK_RETVAL(MPI_Start(&request)); + + for (i = 0; i < send_partitions; ++i) { + for (j = 0; j < COUNT; ++j) message[j+(COUNT*i)] = j+(COUNT*i); + CHECK_RETVAL(MPI_Pready(i, request)); + } + + while (!flag) { + CHECK_RETVAL(MPI_Test(&request, &flag, MPI_STATUS_IGNORE)); + } + + MPI_Request_free(&request); + + } + else if (1 == myrank) { + MPI_Precv_init(message, recv_partitions, COUNT, MPI_INT, source, tag, MPI_COMM_WORLD , MPI_INFO_NULL , &request); + MPI_Start(&request); + while (!flag) { + MPI_Test(&request, &flag, MPI_STATUS_IGNORE); + } + + MPI_Request_free(&request); + + /* all partitions received; check contents */ + for (i = 0; i < PARTITIONS*COUNT; ++i) { + if (message[i] != i) { + fprintf(stderr, "ERROR: Contents received do not match contents sent (expected %d, found %d).\n",i,message[i]); + MPI_Abort(MPI_COMM_WORLD, 1); + } + } + } + + MPI_Barrier(MPI_COMM_WORLD); + if (0 == myrank) {TEST_RAN_TO_COMPLETION();} + + MPI_Finalize(); + + return 0; +} + diff --git a/partitioned-communication/test_partitions2.c b/partitioned-communication/test_partitions2.c new file mode 100644 index 0000000..053ae32 --- /dev/null +++ b/partitioned-communication/test_partitions2.c @@ -0,0 +1,78 @@ +/* + * Initializes using an invalid number of partitions: Precv_init with 0 partitions. + * + * P. 114, line 13-14: Valid partitioned communication operations must have one or more partitions specified. + * P. 117, line 15-16: It is erroneous to provide a partitions value <= 0. + * P. 118, line 28: It is erroneous to provide a partitions value <= 0. + * + * Expected outcome: Error message + */ + +#include "test_common.h" + +#define PARTITIONS 8 +#define COUNT 5 + +int main(int argc, char *argv[]) { + + int message[PARTITIONS*COUNT]; + + MPI_Count send_partitions = PARTITIONS; + MPI_Count recv_partitions = 0; /* bad number of partitions */ + + int source = 0, dest = 1, tag = 1, flag = 0; + int myrank, i, j; + int provided; + + for (i = 0; i < PARTITIONS * COUNT; ++i) message[i] = 0; + + MPI_Request request; + MPI_Init_thread(&argc, &argv, MPI_THREAD_SERIALIZED, &provided); + if (provided < MPI_THREAD_SERIALIZED) MPI_Abort(MPI_COMM_WORLD , EXIT_FAILURE); MPI_Comm_rank(MPI_COMM_WORLD , &myrank); + + if (0 == myrank) { + + MPI_Psend_init(message, send_partitions, COUNT, MPI_INT, dest, tag, MPI_COMM_WORLD , MPI_INFO_NULL , &request); + MPI_Start(&request); + + for (i = 0; i < send_partitions; ++i) { + for (j = 0; j < COUNT; ++j) message[j+(COUNT*i)] = j+(COUNT*i); + MPI_Pready(i, request); + } + + while (!flag) { + MPI_Test(&request, &flag, MPI_STATUS_IGNORE); + } + + MPI_Request_free(&request); + + } + else if (1 == myrank) { + fprintf(stderr, "Rank 1 initializing recv partitions to %d\n", recv_partitions); + /* Error should occur with this call */ + CHECK_RETVAL(MPI_Precv_init(message, recv_partitions, COUNT, MPI_INT, source, tag, MPI_COMM_WORLD , MPI_INFO_NULL , &request)); + + CHECK_RETVAL( MPI_Start(&request)); + while (!flag) { + CHECK_RETVAL(MPI_Test(&request, &flag, MPI_STATUS_IGNORE)); + } + + MPI_Request_free(&request); + + /* all partitions received; check contents */ + for (i = 0; i < PARTITIONS*COUNT; ++i) { + if (message[i] != i) { + fprintf(stderr, "ERROR: Contents received do not match contents sent (expected %d, found %d).\n",i,message[i]); + MPI_Abort(MPI_COMM_WORLD, 1); + } + } + } + + MPI_Barrier(MPI_COMM_WORLD); + if (0 == myrank) {TEST_RAN_TO_COMPLETION();} + + MPI_Finalize(); + + return 0; +} + diff --git a/partitioned-communication/test_partitions3.c b/partitioned-communication/test_partitions3.c new file mode 100644 index 0000000..dea2ba8 --- /dev/null +++ b/partitioned-communication/test_partitions3.c @@ -0,0 +1,83 @@ +// Copyright 2024 National Technology & Engineering Solutions of Sandia, LLC +// (NTESS). Under the terms of Contract DE-NA0003525 with NTESS, the U.S. +// Government retains certain rights in this software. + +/* + * Initializes using an invalid number of partitions: Precv_init with negative partitions. + * + * P. 114, line 13-14: Valid partitioned communication operations must have one or more partitions specified. + * P. 117, line 15-16: It is erroneous to provide a partitions value <= 0. + * P. 118, line 28: It is erroneous to provide a partitions value <= 0. + * + * Expected outcome: Error message + */ + +#include "test_common.h" + +#define PARTITIONS 8 +#define COUNT 5 + +int main(int argc, char *argv[]) { + + int message[PARTITIONS*COUNT]; + + MPI_Count send_partitions = PARTITIONS; + MPI_Count recv_partitions = -3; /* Bad number of partitions */ + + int source = 0, dest = 1, tag = 1, flag = 0; + int myrank, i, j; + int provided; + + for (i = 0; i < PARTITIONS * COUNT; ++i) message[i] = 0; + + MPI_Request request; + MPI_Init_thread(&argc, &argv, MPI_THREAD_SERIALIZED, &provided); + if (provided < MPI_THREAD_SERIALIZED) MPI_Abort(MPI_COMM_WORLD , EXIT_FAILURE); MPI_Comm_rank(MPI_COMM_WORLD , &myrank); + + if (0 == myrank) { + + MPI_Psend_init(message, send_partitions, COUNT, MPI_INT, dest, tag, MPI_COMM_WORLD , MPI_INFO_NULL , &request); + + MPI_Start(&request); + + for (i = 0; i < send_partitions; ++i) { + for (j = 0; j < COUNT; ++j) message[j+(COUNT*i)] = j+(COUNT*i); + MPI_Pready(i, request); + } + + while (!flag) { + MPI_Test(&request, &flag, MPI_STATUS_IGNORE); + } + + MPI_Request_free(&request); + + } + else if (1 == myrank) { + fprintf(stderr, "Rank 1 initializing recv partitions to %d\n", recv_partitions); + /* Error should occur with this call */ + CHECK_RETVAL(MPI_Precv_init(message, recv_partitions, COUNT, MPI_INT, source, tag, MPI_COMM_WORLD , MPI_INFO_NULL , &request)); + + CHECK_RETVAL(MPI_Start(&request)); + while (!flag) { + CHECK_RETVAL(MPI_Test(&request, &flag, MPI_STATUS_IGNORE)); + } + + MPI_Request_free(&request); + + /* all partitions received; check contents */ + for (i = 0; i < PARTITIONS*COUNT; ++i) { + if (message[i] != i) { + fprintf(stderr, "ERROR: Contents received do not match contents sent (expected %d, found %d).\n",i,message[i]); + MPI_Abort(MPI_COMM_WORLD, 1); + } + } + } + + MPI_Barrier(MPI_COMM_WORLD); + if (0 == myrank) {TEST_RAN_TO_COMPLETION();} + + MPI_Finalize(); + + return 0; +} + diff --git a/partitioned-communication/test_pready0.c b/partitioned-communication/test_pready0.c new file mode 100644 index 0000000..ec2bbf0 --- /dev/null +++ b/partitioned-communication/test_pready0.c @@ -0,0 +1,82 @@ +// Copyright 2024 National Technology & Engineering Solutions of Sandia, LLC +// (NTESS). Under the terms of Contract DE-NA0003525 with NTESS, the U.S. +// Government retains certain rights in this software. + +/* + * Calls pready on a partition whose index is greater than the upper bound. + * + * P. 119, line 7-9: "Partition numbering ranges from 0 to 1 less than number of partitions declared in the MPI_PSEND_INIT call, and specifying a partition number that is equal to or larger is erroneous." + * + * Expected outcome: Error message + */ + +#include "test_common.h" + +#define PARTITIONS 8 +#define COUNT 5 + +int main(int argc, char *argv[]) { + + int message[PARTITIONS*COUNT]; + + MPI_Count partitions = PARTITIONS; + MPI_Count bad_partition = PARTITIONS + 5; + + int source = 0, dest = 1, tag = 1, flag = 0; + int myrank, i, j; + int provided; + + for (i = 0; i < PARTITIONS * COUNT; ++i) message[i] = 0; + + MPI_Request request; + MPI_Init_thread(&argc, &argv, MPI_THREAD_SERIALIZED, &provided); + if (provided < MPI_THREAD_SERIALIZED) MPI_Abort(MPI_COMM_WORLD , EXIT_FAILURE); MPI_Comm_rank(MPI_COMM_WORLD , &myrank); + + if (0 == myrank) { + + CHECK_RETVAL(MPI_Psend_init(message, partitions, COUNT, MPI_INT, dest, tag, MPI_COMM_WORLD , MPI_INFO_NULL , &request)); + CHECK_RETVAL(MPI_Start(&request)); + + for (i = 0; i < partitions; ++i) { + for (j = 0; j < COUNT; ++j) message[j+(COUNT*i)] = j+(COUNT*i); + CHECK_RETVAL(MPI_Pready(i, request)); + } + + /* Call Pready on a nonexistant partition; an error should occur */ + fprintf(stderr, "Marking invalid partition ready...\n"); + CHECK_RETVAL(MPI_Pready(bad_partition, request)); + fprintf(stderr, "Invalid partition marked ready...\n"); + + while (!flag) { + MPI_Test(&request, &flag, MPI_STATUS_IGNORE); + } + + MPI_Request_free(&request); + + } + else if (1 == myrank) { + MPI_Precv_init(message, partitions, COUNT, MPI_INT, source, tag, MPI_COMM_WORLD , MPI_INFO_NULL , &request); + MPI_Start(&request); + while (!flag) { + MPI_Test(&request, &flag, MPI_STATUS_IGNORE); + } + + MPI_Request_free(&request); + + /* all partitions received; check contents */ + for (i = 0; i < PARTITIONS*COUNT; ++i) { + if (message[i] != i) { + fprintf(stderr, "ERROR: Contents received do not match contents sent (expected %d, found %d).\n",i,message[i]); + MPI_Abort(MPI_COMM_WORLD, 1); + } + } + } + + MPI_Barrier(MPI_COMM_WORLD); + if (0 == myrank) {TEST_RAN_TO_COMPLETION();} + + MPI_Finalize(); + + return 0; +} + diff --git a/partitioned-communication/test_pready1.c b/partitioned-communication/test_pready1.c new file mode 100644 index 0000000..8d657f2 --- /dev/null +++ b/partitioned-communication/test_pready1.c @@ -0,0 +1,82 @@ +// Copyright 2024 National Technology & Engineering Solutions of Sandia, LLC +// (NTESS). Under the terms of Contract DE-NA0003525 with NTESS, the U.S. +// Government retains certain rights in this software. + +/* + * Calls pready on a partition whose index is equal to the number of partitions. + * + * P. 119, line 7-9: "Partition numbering ranges from 0 to 1 less than number of partitions declared in the MPI_PSEND_INIT call, and specifying a partition number that is equal to or larger is erroneous." + * + * Expected outcome: Error message + */ + +#include "test_common.h" + +#define PARTITIONS 8 +#define COUNT 5 + +int main(int argc, char *argv[]) { + + int message[PARTITIONS*COUNT]; + + MPI_Count partitions = PARTITIONS; + MPI_Count bad_partition = PARTITIONS; + + int source = 0, dest = 1, tag = 1, flag = 0; + int myrank, i, j; + int provided; + + for (i = 0; i < PARTITIONS * COUNT; ++i) message[i] = 0; + + MPI_Request request; + MPI_Init_thread(&argc, &argv, MPI_THREAD_SERIALIZED, &provided); + if (provided < MPI_THREAD_SERIALIZED) MPI_Abort(MPI_COMM_WORLD , EXIT_FAILURE); MPI_Comm_rank(MPI_COMM_WORLD , &myrank); + + if (0 == myrank) { + + CHECK_RETVAL(MPI_Psend_init(message, partitions, COUNT, MPI_INT, dest, tag, MPI_COMM_WORLD , MPI_INFO_NULL , &request)); + CHECK_RETVAL(MPI_Start(&request)); + + for (i = 0; i < partitions; ++i) { + for (j = 0; j < COUNT; ++j) message[j+(COUNT*i)] = j+(COUNT*i); + CHECK_RETVAL(MPI_Pready(i, request)); + } + + /* Call Pready on a nonexistant partition; an error should occur */ + fprintf(stderr, "Marking invalid partition ready...\n"); + CHECK_RETVAL(MPI_Pready(bad_partition, request)); + fprintf(stderr, "Invalid partition marked ready...\n"); + + while (!flag) { + MPI_Test(&request, &flag, MPI_STATUS_IGNORE); + } + + MPI_Request_free(&request); + + } + else if (1 == myrank) { + MPI_Precv_init(message, partitions, COUNT, MPI_INT, source, tag, MPI_COMM_WORLD , MPI_INFO_NULL , &request); + MPI_Start(&request); + while (!flag) { + MPI_Test(&request, &flag, MPI_STATUS_IGNORE); + } + + MPI_Request_free(&request); + + /* all partitions received; check contents */ + for (i = 0; i < PARTITIONS*COUNT; ++i) { + if (message[i] != i) { + fprintf(stderr, "ERROR: Contents received do not match contents sent (expected %d, found %d).\n",i,message[i]); + MPI_Abort(MPI_COMM_WORLD, 1); + } + } + } + + MPI_Barrier(MPI_COMM_WORLD); + if (0 == myrank) {TEST_RAN_TO_COMPLETION();} + + MPI_Finalize(); + + return 0; +} + diff --git a/partitioned-communication/test_pready2.c b/partitioned-communication/test_pready2.c new file mode 100644 index 0000000..913bec9 --- /dev/null +++ b/partitioned-communication/test_pready2.c @@ -0,0 +1,82 @@ +// Copyright 2024 National Technology & Engineering Solutions of Sandia, LLC +// (NTESS). Under the terms of Contract DE-NA0003525 with NTESS, the U.S. +// Government retains certain rights in this software. + +/* + * Calls pready on a partition whose index is negative. + * + * P. 119, line 7-9: "Partition numbering ranges from 0 to 1 less than number of partitions declared in the MPI_PSEND_INIT call, and specifying a partition number that is equal to or larger is erroneous." + * + * Expected outcome: Error message + */ + +#include "test_common.h" + +#define PARTITIONS 8 +#define COUNT 5 + +int main(int argc, char *argv[]) { + + int message[PARTITIONS*COUNT]; + + MPI_Count partitions = PARTITIONS; + MPI_Count bad_partition = -5; + + int source = 0, dest = 1, tag = 1, flag = 0; + int myrank, i, j; + int provided; + + for (i = 0; i < PARTITIONS * COUNT; ++i) message[i] = 0; + + MPI_Request request; + MPI_Init_thread(&argc, &argv, MPI_THREAD_SERIALIZED, &provided); + if (provided < MPI_THREAD_SERIALIZED) MPI_Abort(MPI_COMM_WORLD , EXIT_FAILURE); MPI_Comm_rank(MPI_COMM_WORLD , &myrank); + + if (0 == myrank) { + + CHECK_RETVAL(MPI_Psend_init(message, partitions, COUNT, MPI_INT, dest, tag, MPI_COMM_WORLD , MPI_INFO_NULL , &request)); + CHECK_RETVAL(MPI_Start(&request)); + + for (i = 0; i < partitions; ++i) { + for (j = 0; j < COUNT; ++j) message[j+(COUNT*i)] = j+(COUNT*i); + CHECK_RETVAL(MPI_Pready(i, request)); + } + + /* Call Pready on a nonexistant partition; an error should occur */ + fprintf(stderr, "Marking invalid partition ready...\n"); + CHECK_RETVAL(MPI_Pready(bad_partition, request)); + fprintf(stderr, "Invalid partition marked ready...\n"); + + while (!flag) { + MPI_Test(&request, &flag, MPI_STATUS_IGNORE); + } + + MPI_Request_free(&request); + + } + else if (1 == myrank) { + MPI_Precv_init(message, partitions, COUNT, MPI_INT, source, tag, MPI_COMM_WORLD , MPI_INFO_NULL , &request); + MPI_Start(&request); + while (!flag) { + MPI_Test(&request, &flag, MPI_STATUS_IGNORE); + } + + MPI_Request_free(&request); + + /* all partitions received; check contents */ + for (i = 0; i < PARTITIONS*COUNT; ++i) { + if (message[i] != i) { + fprintf(stderr, "ERROR: Contents received do not match contents sent (expected %d, found %d).\n",i,message[i]); + MPI_Abort(MPI_COMM_WORLD, 1); + } + } + } + + MPI_Barrier(MPI_COMM_WORLD); + if (0 == myrank) {TEST_RAN_TO_COMPLETION();} + + MPI_Finalize(); + + return 0; +} + diff --git a/partitioned-communication/test_pready3.c b/partitioned-communication/test_pready3.c new file mode 100644 index 0000000..7b24394 --- /dev/null +++ b/partitioned-communication/test_pready3.c @@ -0,0 +1,82 @@ +// Copyright 2024 National Technology & Engineering Solutions of Sandia, LLC +// (NTESS). Under the terms of Contract DE-NA0003525 with NTESS, the U.S. +// Government retains certain rights in this software. + +/* + * Calls pready on a partition that is active, i.e., has already been marked as ready. + * + * P. 119, line 11-12: Calling MPI_PREADY on an active partition is erroneous. + * + * Expected outcome: Error message + */ + +#include "test_common.h" + +#define PARTITIONS 8 +#define COUNT 5 + +int main(int argc, char *argv[]) { + + int message[PARTITIONS*COUNT]; + + MPI_Count partitions = PARTITIONS; + MPI_Count active_partition = 2; // assumes at least 3 partitions + + int source = 0, dest = 1, tag = 1, flag = 0; + int myrank, i, j; + int provided; + + for (i = 0; i < PARTITIONS * COUNT; ++i) message[i] = 0; + + MPI_Request request; + MPI_Init_thread(&argc, &argv, MPI_THREAD_SERIALIZED, &provided); + if (provided < MPI_THREAD_SERIALIZED) MPI_Abort(MPI_COMM_WORLD , EXIT_FAILURE); MPI_Comm_rank(MPI_COMM_WORLD , &myrank); + + if (0 == myrank) { + + CHECK_RETVAL(MPI_Psend_init(message, partitions, COUNT, MPI_INT, dest, tag, MPI_COMM_WORLD , MPI_INFO_NULL , &request)); + CHECK_RETVAL(MPI_Start(&request)); + + for (i = 0; i < partitions; ++i) { + for (j = 0; j < COUNT; ++j) message[j+(COUNT*i)] = j+(COUNT*i); + CHECK_RETVAL(MPI_Pready(i, request)); + } + + /* Call Pready on a partition already marked as ready; an error should occur */ + fprintf(stderr, "Marking previously marked partition ready...\n"); + CHECK_RETVAL(MPI_Pready(active_partition, request)); + fprintf(stderr, "Previously marked partition marked again...\n"); + + while (!flag) { + MPI_Test(&request, &flag, MPI_STATUS_IGNORE); + } + + MPI_Request_free(&request); + + } + else if (1 == myrank) { + MPI_Precv_init(message, partitions, COUNT, MPI_INT, source, tag, MPI_COMM_WORLD , MPI_INFO_NULL , &request); + MPI_Start(&request); + while (!flag) { + MPI_Test(&request, &flag, MPI_STATUS_IGNORE); + } + + MPI_Request_free(&request); + + /* all partitions received; check contents */ + for (i = 0; i < PARTITIONS*COUNT; ++i) { + if (message[i] != i) { + fprintf(stderr, "ERROR: Contents received do not match contents sent (expected %d, found %d).\n",i,message[i]); + MPI_Abort(MPI_COMM_WORLD, 1); + } + } + } + + MPI_Barrier(MPI_COMM_WORLD); + if (0 == myrank) {TEST_RAN_TO_COMPLETION();} + + MPI_Finalize(); + + return 0; +} + diff --git a/partitioned-communication/test_pready4.c b/partitioned-communication/test_pready4.c new file mode 100644 index 0000000..309cdca --- /dev/null +++ b/partitioned-communication/test_pready4.c @@ -0,0 +1,61 @@ +// Copyright 2024 National Technology & Engineering Solutions of Sandia, LLC +// (NTESS). Under the terms of Contract DE-NA0003525 with NTESS, the U.S. +// Government retains certain rights in this software. + +/* + * Calls PREADY on a request object that corresponds to an Isend. + * + * P. 119, line 6: "It is erroneous to use MPI_PREADY on any request object that does not correspond to a partitioned send operation." + * + * Expected outcome: Error message + */ + +#include "test_common.h" + +#define PARTITIONS 8 +#define COUNT 5 + +int main(int argc, char *argv[]) { + + int message[PARTITIONS*COUNT]; + + MPI_Count partitions = PARTITIONS; + + int source = 0, dest = 1, tag = 1, flag = 0; + int myrank, i, j; + int provided; + + for (i = 0; i < PARTITIONS * COUNT; ++i) message[i] = 0; + + MPI_Request request; + MPI_Request isend_request; + MPI_Init_thread(&argc, &argv, MPI_THREAD_SERIALIZED, &provided); + if (provided < MPI_THREAD_SERIALIZED) MPI_Abort(MPI_COMM_WORLD , EXIT_FAILURE); MPI_Comm_rank(MPI_COMM_WORLD , &myrank); + + if (0 == myrank) { + + CHECK_RETVAL(MPI_Isend(message, PARTITIONS*COUNT, MPI_INT, dest, tag, MPI_COMM_WORLD, &isend_request)); + + + // This call is erroneous + CHECK_RETVAL(MPI_Pready(0, isend_request)); + + CHECK_RETVAL(MPI_Wait(&isend_request, MPI_STATUS_IGNORE)); + + } + else if (1 == myrank) { + + MPI_Irecv(message, PARTITIONS*COUNT, MPI_INT, source, tag, MPI_COMM_WORLD, &request); + + MPI_Wait(&request, MPI_STATUS_IGNORE); + + } + + MPI_Barrier(MPI_COMM_WORLD); + if (0 == myrank) {TEST_RAN_TO_COMPLETION();} + + MPI_Finalize(); + + return 0; +} + diff --git a/partitioned-communication/test_pready_list0.c b/partitioned-communication/test_pready_list0.c new file mode 100644 index 0000000..6bed784 --- /dev/null +++ b/partitioned-communication/test_pready_list0.c @@ -0,0 +1,82 @@ +// Copyright 2024 National Technology & Engineering Solutions of Sandia, LLC +// (NTESS). Under the terms of Contract DE-NA0003525 with NTESS, the U.S. +// Government retains certain rights in this software. + +/* + * P. 119-120: PREADY_LIST defined here. + * + * This test just uses PREADY_LIST instead of PREADY + * + * Expected outcome: PASS + * + */ + +#include "test_common.h" + +#define PARTITIONS 8 +#define COUNT 5 + +int main(int argc, char *argv[]) { + + int message[PARTITIONS*COUNT]; + + MPI_Count partitions = PARTITIONS; + int partitions_list[PARTITIONS]; + + int source = 0, dest = 1, tag = 1, flag = 0; + int myrank, i, j; + int provided; + + for (i = 0; i < PARTITIONS * COUNT; ++i) message[i] = 0; + + MPI_Request request; + MPI_Init_thread(&argc, &argv, MPI_THREAD_SERIALIZED, &provided); + if (provided < MPI_THREAD_SERIALIZED) MPI_Abort(MPI_COMM_WORLD , EXIT_FAILURE); MPI_Comm_rank(MPI_COMM_WORLD , &myrank); + + if (0 == myrank) { + + CHECK_RETVAL(MPI_Psend_init(message, partitions, COUNT, MPI_INT, dest, tag, MPI_COMM_WORLD , MPI_INFO_NULL , &request)); + CHECK_RETVAL(MPI_Start(&request)); + + /* Fill the sending partitions and mark as ready as each is filled */ + for (i = 0; i < partitions; ++i) { + for (j = 0; j < COUNT; ++j) message[j+(COUNT*i)] = j+(COUNT*i); + partitions_list[i] = i; + } + + /* Basic Pready list use case */ + CHECK_RETVAL(MPI_Pready_list(partitions, partitions_list, request)); + + while (!flag) { + CHECK_RETVAL(MPI_Test(&request, &flag, MPI_STATUS_IGNORE)); + } + + MPI_Request_free(&request); + + } + else if (1 == myrank) { + CHECK_RETVAL(MPI_Precv_init(message, partitions, COUNT, MPI_INT, source, tag, MPI_COMM_WORLD , MPI_INFO_NULL , &request)); + CHECK_RETVAL(MPI_Start(&request)); + while (!flag) { + CHECK_RETVAL(MPI_Test(&request, &flag, MPI_STATUS_IGNORE)); + } + + MPI_Request_free(&request); + + /* all partitions received; check contents */ + for (i = 0; i < PARTITIONS*COUNT; ++i) { + if (message[i] != i) { + fprintf(stderr, "ERROR: Contents received do not match contents sent (expected %d, found %d).\n",i,message[i]); + MPI_Abort(MPI_COMM_WORLD, 1); + } + } + } + + MPI_Barrier(MPI_COMM_WORLD); + if (0 == myrank) {TEST_RAN_TO_COMPLETION();} + + MPI_Finalize(); + + return 0; +} + diff --git a/partitioned-communication/test_pready_list1.c b/partitioned-communication/test_pready_list1.c new file mode 100644 index 0000000..0750b8f --- /dev/null +++ b/partitioned-communication/test_pready_list1.c @@ -0,0 +1,86 @@ +// Copyright 2024 National Technology & Engineering Solutions of Sandia, LLC +// (NTESS). Under the terms of Contract DE-NA0003525 with NTESS, the U.S. +// Government retains certain rights in this software. + +/* + * P. 119-120: PREADY_LIST defined here. + * The discussion of PREADY (P. 119, line 11-12) implies the same partition cannot occur more than once in a list, so that is checked here + * + * Expected outcome: Some sort of error + * + */ + +#include "test_common.h" + +#define PARTITIONS 8 +#define COUNT 5 + +int main(int argc, char *argv[]) { + + int message[PARTITIONS*COUNT]; + + MPI_Count partitions = PARTITIONS; + int partitions_list[PARTITIONS]; + + int source = 0, dest = 1, tag = 1, flag = 0; + int myrank, i, j; + int provided; + + for (i = 0; i < PARTITIONS * COUNT; ++i) message[i] = 0; + + MPI_Request request; + MPI_Init_thread(&argc, &argv, MPI_THREAD_SERIALIZED, &provided); + if (provided < MPI_THREAD_SERIALIZED) MPI_Abort(MPI_COMM_WORLD , EXIT_FAILURE); MPI_Comm_rank(MPI_COMM_WORLD , &myrank); + + if (0 == myrank) { + + CHECK_RETVAL(MPI_Psend_init(message, partitions, COUNT, MPI_INT, dest, tag, MPI_COMM_WORLD , MPI_INFO_NULL , &request)); + CHECK_RETVAL(MPI_Start(&request)); + + /* Fill the sending partitions and mark as ready as each is filled */ + for (i = 0; i < partitions; ++i) { + for (j = 0; j < COUNT; ++j) message[j+(COUNT*i)] = j+(COUNT*i); + partitions_list[i] = i; + } + + /* make is so the same partition appears more than once in the list */ + partitions_list[0] = PARTITIONS-1; + + /* this is erroneous */ + CHECK_RETVAL(MPI_Pready_list(partitions, partitions_list, request)); + /* this will keeping the test from hanging if an error did not occur */ + MPI_Pready(0, request); + + while (!flag) { + CHECK_RETVAL(MPI_Test(&request, &flag, MPI_STATUS_IGNORE)); + } + + MPI_Request_free(&request); + + } + else if (1 == myrank) { + CHECK_RETVAL(MPI_Precv_init(message, partitions, COUNT, MPI_INT, source, tag, MPI_COMM_WORLD , MPI_INFO_NULL , &request)); + CHECK_RETVAL(MPI_Start(&request)); + while (!flag) { + CHECK_RETVAL(MPI_Test(&request, &flag, MPI_STATUS_IGNORE)); + } + + MPI_Request_free(&request); + + /* all partitions received; check contents */ + for (i = 0; i < PARTITIONS*COUNT; ++i) { + if (message[i] != i) { + fprintf(stderr, "ERROR: Contents received do not match contents sent (expected %d, found %d).\n",i,message[i]); + MPI_Abort(MPI_COMM_WORLD, 1); + } + } + } + + MPI_Barrier(MPI_COMM_WORLD); + if (0 == myrank) {TEST_RAN_TO_COMPLETION();} + + MPI_Finalize(); + + return 0; +} + diff --git a/partitioned-communication/test_pready_range0.c b/partitioned-communication/test_pready_range0.c new file mode 100644 index 0000000..61ead3b --- /dev/null +++ b/partitioned-communication/test_pready_range0.c @@ -0,0 +1,81 @@ +// Copyright 2024 National Technology & Engineering Solutions of Sandia, LLC +// (NTESS). Under the terms of Contract DE-NA0003525 with NTESS, the U.S. +// Government retains certain rights in this software. + +/* + * P. 110: Definition of MPI_PREADY_RANGE. It is inclusive (line 36) + * + * This test just uses PREADY_RANGE instead of PREADY + * + * Expected outcome: PASS + * + */ + +#include "test_common.h" + +#define PARTITIONS 8 +#define COUNT 5 + +int main(int argc, char *argv[]) { + + int message[PARTITIONS*COUNT]; + + MPI_Count partitions = PARTITIONS; + + int source = 0, dest = 1, tag = 1, flag = 0; + int myrank, i, j; + int provided; + + for (i = 0; i < PARTITIONS * COUNT; ++i) message[i] = 0; + + MPI_Request request; + MPI_Init_thread(&argc, &argv, MPI_THREAD_SERIALIZED, &provided); + if (provided < MPI_THREAD_SERIALIZED) MPI_Abort(MPI_COMM_WORLD , EXIT_FAILURE); MPI_Comm_rank(MPI_COMM_WORLD , &myrank); + + if (0 == myrank) { + + CHECK_RETVAL(MPI_Psend_init(message, partitions, COUNT, MPI_INT, dest, tag, MPI_COMM_WORLD , MPI_INFO_NULL , &request)); + CHECK_RETVAL(MPI_Start(&request)); + + /* Fill the sending partitions*/ + for (i = 0; i < partitions; ++i) { + for (j = 0; j < COUNT; ++j) message[j+(COUNT*i)] = j+(COUNT*i); + // CHECK_RETVAL(MPI_Pready(i, request)); + } + + /* Basic Pready range use case */ + CHECK_RETVAL(MPI_Pready_range(0,partitions-1,request)); + + while (!flag) { + CHECK_RETVAL(MPI_Test(&request, &flag, MPI_STATUS_IGNORE)); + } + + MPI_Request_free(&request); + + } + else if (1 == myrank) { + CHECK_RETVAL(MPI_Precv_init(message, partitions, COUNT, MPI_INT, source, tag, MPI_COMM_WORLD , MPI_INFO_NULL , &request)); + CHECK_RETVAL(MPI_Start(&request)); + while (!flag) { + CHECK_RETVAL(MPI_Test(&request, &flag, MPI_STATUS_IGNORE)); + } + + MPI_Request_free(&request); + + /* all partitions received; check contents */ + for (i = 0; i < PARTITIONS*COUNT; ++i) { + if (message[i] != i) { + fprintf(stderr, "ERROR: Contents received do not match contents sent (expected %d, found %d).\n",i,message[i]); + MPI_Abort(MPI_COMM_WORLD, 1); + } + } + } + + MPI_Barrier(MPI_COMM_WORLD); + if (0 == myrank) {TEST_RAN_TO_COMPLETION();} + + MPI_Finalize(); + + return 0; +} + diff --git a/partitioned-communication/test_startall0.c b/partitioned-communication/test_startall0.c new file mode 100644 index 0000000..41a0ad7 --- /dev/null +++ b/partitioned-communication/test_startall0.c @@ -0,0 +1,160 @@ +// Copyright 2024 National Technology & Engineering Solutions of Sandia, LLC +// (NTESS). Under the terms of Contract DE-NA0003525 with NTESS, the U.S. +// Government retains certain rights in this software. + +/* + * STARTALL should start multiple channels, since it is a generalization of MPI_START. (Section 3.9, pp. 109-110). + * + * This test simply confirms this. + * + * Expected outcome: PASS + * + */ + +#include "test_common.h" + +#define PARTITIONS 8 +#define COUNT 5 +#define CHANNELS 3 + +int main(int argc, char *argv[]) { + + int messageA[PARTITIONS*COUNT]; + int messageB[PARTITIONS*COUNT]; + int messageC[PARTITIONS*COUNT]; + + MPI_Count partitions = PARTITIONS; + + int source = 0, dest = 1, tagA = 1, tagB = 2, tagC = 3, flagA = 0, flagB = 0, flagC = 0, sflag = 0; + int myrank, i, j; + int provided; + + for (i = 0; i < PARTITIONS * COUNT; ++i) { + messageA[i] = 0; + messageB[i] = 0; + messageC[i] = 0; + } + + MPI_Request requests[CHANNELS]; + MPI_Status status; + MPI_Init_thread(&argc, &argv, MPI_THREAD_SERIALIZED, &provided); + if (provided < MPI_THREAD_SERIALIZED) MPI_Abort(MPI_COMM_WORLD , EXIT_FAILURE); + MPI_Comm_rank(MPI_COMM_WORLD , &myrank); + + if (0 == myrank) { + + CHECK_RETVAL(MPI_Psend_init(messageA, partitions, COUNT, MPI_INT, dest, tagA, MPI_COMM_WORLD, MPI_INFO_NULL, &requests[0])); + CHECK_RETVAL(MPI_Psend_init(messageB, partitions, COUNT, MPI_INT, dest, tagB, MPI_COMM_WORLD, MPI_INFO_NULL, &requests[1])); + CHECK_RETVAL(MPI_Psend_init(messageC, partitions, COUNT, MPI_INT, dest, tagC, MPI_COMM_WORLD, MPI_INFO_NULL, &requests[2])); + + sflag = 0; + MPI_Request_get_status(requests[0], &sflag, &status); + fprintf(stderr, "After send init: request 0: %d\n", sflag); + sflag = 0; + MPI_Request_get_status(requests[1], &sflag, &status); + fprintf(stderr, "After send init: request 1: %d\n", sflag); + sflag = 0; + MPI_Request_get_status(requests[2], &sflag, &status); + fprintf(stderr, "After send init: request 2: %d\n", sflag); + + CHECK_RETVAL(MPI_Startall(CHANNELS, requests)); + + sflag = 0; + MPI_Request_get_status(requests[0], &sflag, &status); + fprintf(stderr, "After startall: request 0: %d\n", sflag); + sflag = 0; + MPI_Request_get_status(requests[1], &sflag, &status); + fprintf(stderr, "After startall: request 1: %d\n", sflag); + sflag = 0; + MPI_Request_get_status(requests[2], &sflag, &status); + fprintf(stderr, "After startall: request 2: %d\n", sflag); + + /* Fill the sending partitions and mark as ready as each is filled */ + for (i = 0; i < partitions; ++i) { + for (j = 0; j < COUNT; ++j) messageA[j+(COUNT*i)] = j+(COUNT*i); + CHECK_RETVAL(MPI_Pready(i, requests[0])); + for (j = 0; j < COUNT; ++j) messageB[j+(COUNT*i)] = (j+(COUNT*i)) * 10; + CHECK_RETVAL(MPI_Pready(i, requests[1])); + for (j = 0; j < COUNT; ++j) messageC[j+(COUNT*i)] = (j+(COUNT*i)) * 100; + CHECK_RETVAL(MPI_Pready(i, requests[2])); + } + + sflag = 0; + MPI_Request_get_status(requests[0], &sflag, &status); + fprintf(stderr, "After pready: request 0: %d\n", sflag); + sflag = 0; + MPI_Request_get_status(requests[1], &sflag, &status); + fprintf(stderr, "After pready: request 1: %d\n", sflag); + sflag = 0; + MPI_Request_get_status(requests[2], &sflag, &status); + fprintf(stderr, "After pready: request 2: %d\n", sflag); + + + fprintf(stderr, "Rank 0: testing\n"); + + while ( !(flagA && flagB && flagC) ) { + CHECK_RETVAL(MPI_Test(&requests[0], &flagA, MPI_STATUS_IGNORE)); + CHECK_RETVAL(MPI_Test(&requests[1], &flagB, MPI_STATUS_IGNORE)); + CHECK_RETVAL(MPI_Test(&requests[2], &flagC, MPI_STATUS_IGNORE)); + } + + fprintf(stderr, "Rank 1: all flags True\n"); + + MPI_Request_free(&requests[0]); + MPI_Request_free(&requests[1]); + MPI_Request_free(&requests[2]); + + } + else if (1 == myrank) { + CHECK_RETVAL(MPI_Precv_init(messageA, partitions, COUNT, MPI_INT, source, tagA, MPI_COMM_WORLD, MPI_INFO_NULL, &requests[0])); + CHECK_RETVAL(MPI_Precv_init(messageB, partitions, COUNT, MPI_INT, source, tagB, MPI_COMM_WORLD, MPI_INFO_NULL, &requests[1])); + CHECK_RETVAL(MPI_Precv_init(messageC, partitions, COUNT, MPI_INT, source, tagC, MPI_COMM_WORLD, MPI_INFO_NULL, &requests[2])); + + CHECK_RETVAL(MPI_Startall(CHANNELS, requests)); + + fprintf(stderr, "Rank 1: testing\n"); + while ( !(flagA && flagB && flagC) ) { + CHECK_RETVAL(MPI_Test(&requests[0], &flagA, MPI_STATUS_IGNORE)); + CHECK_RETVAL(MPI_Test(&requests[1], &flagB, MPI_STATUS_IGNORE)); + CHECK_RETVAL(MPI_Test(&requests[2], &flagC, MPI_STATUS_IGNORE)); + } + + MPI_Request_free(&requests[0]); + MPI_Request_free(&requests[1]); + MPI_Request_free(&requests[2]); + + fprintf(stderr, "Message A: "); + for (i=0; i< PARTITIONS*COUNT; ++i) fprintf(stderr, "%d ", messageA[i]); + fprintf(stderr, "\n"); + fprintf(stderr, "Message B: "); + for (i=0; i< PARTITIONS*COUNT; ++i) fprintf(stderr, "%d ", messageB[i]); + fprintf(stderr, "\n"); + fprintf(stderr, "Message C: "); + for (i=0; i< PARTITIONS*COUNT; ++i) fprintf(stderr, "%d ", messageC[i]); + fprintf(stderr, "\n"); + + /* all partitions received; check contents */ + for (i = 0; i < PARTITIONS*COUNT; ++i) { + if (messageA[i] != i) { + fprintf(stderr, "ERROR: Contents of buffer messageA do not match contents sent (expected %d, found %d).\n",i,messageA[i]); + MPI_Abort(MPI_COMM_WORLD, 1); + } + if (messageB[i] != i*10) { + fprintf(stderr, "ERROR: Contents of buffer messageB do not match contents sent (expected %d, found %d).\n",i*10,messageB[i]); + MPI_Abort(MPI_COMM_WORLD, 1); + } + if (messageC[i] != i*100) { + fprintf(stderr, "ERROR: Contents of buffer messageC do not match contents sent (expected %d, found %d).\n",i*100,messageC[i]); + MPI_Abort(MPI_COMM_WORLD, 1); + } + } + } + + MPI_Barrier(MPI_COMM_WORLD); + if (0 == myrank) {TEST_RAN_TO_COMPLETION();} + + MPI_Finalize(); + + return 0; +} + diff --git a/partitioned-communication/test_state0.c b/partitioned-communication/test_state0.c new file mode 100644 index 0000000..f359d82 --- /dev/null +++ b/partitioned-communication/test_state0.c @@ -0,0 +1,151 @@ +// Copyright 2024 National Technology & Engineering Solutions of Sandia, LLC +// (NTESS). Under the terms of Contract DE-NA0003525 with NTESS, the U.S. +// Government retains certain rights in this software. + +/* + * This test is motivated by https://github.com/open-mpi/ompi/issues/12328 + * + * The test confirms that the request status is reset in subsequent rounds using the same Send/Recv channel. + * + * On the sender side, if the request status does not reset with the second call to START, marking partitions + * with PREADY will do nothing, and the TEST/WAIT will automatically succeed with no data having been sent. + * + * On the reciever side, if the request status does not reset with the second call to START, the receiver will + * immediately succeed on TEST/WAIT and then the test will fail when validating the buffer contents (because the + * receiver did not get round 2 data). If the request status is reset, then the test will hang waiting for + * completion. + * + * The test is passed if it runs to completion successfully. + * + */ + +#include "test_common.h" + +#define PARTITIONS 4 +#define COUNT 5 + +int main(int argc, char *argv[]) { + + int message[PARTITIONS*COUNT]; + + MPI_Count partitions = PARTITIONS; + + int source = 0, dest = 1, tag = 1, flag = 0; + int myrank, i, j; + int provided; + + for (i = 0; i < PARTITIONS * COUNT; ++i) message[i] = 0; + + MPI_Request request; + MPI_Status status; + + MPI_Init_thread(&argc, &argv, MPI_THREAD_SERIALIZED, &provided); + if (provided < MPI_THREAD_SERIALIZED) MPI_Abort(MPI_COMM_WORLD , EXIT_FAILURE); MPI_Comm_rank(MPI_COMM_WORLD , &myrank); + + if (0 == myrank) { + + MPI_Psend_init(message, partitions, COUNT, MPI_INT, dest, tag, MPI_COMM_WORLD , MPI_INFO_NULL , &request); + + flag = 0; + MPI_Request_get_status(request, &flag, &status); + fprintf(stderr, "Rank 0: Communication Round 1: After PSEND_INIT, request status is: %d\n", flag); + + /* Round 1 */ + MPI_Start(&request); + + flag = 0; + MPI_Request_get_status(request, &flag, &status); + fprintf(stderr, "Rank 0: Communication Round 1: After START, request status is: %d\n", flag); + + for (i = 0; i < partitions; ++i) { + for (j = 0; j < COUNT; ++j) message[j+(COUNT*i)] = j+(COUNT*i); + MPI_Pready(i, request); + flag = 0; + MPI_Request_get_status(request, &flag, &status); + fprintf(stderr, "Rank 0: Communication Round 1: After PREADY #%d, request status is: %d\n", i, flag); + } + + fprintf(stderr, "Sent in round 1: "); + for (i=0; i < COUNT * PARTITIONS; ++i) fprintf(stderr, " %d", message[i]); + fprintf(stderr, "\n"); + + flag = 0; + while (!flag) { + CHECK_RETVAL(MPI_Test(&request, &flag, MPI_STATUS_IGNORE)); + } + flag = 0; + MPI_Request_get_status(request, &flag, &status); + fprintf(stderr, "Rank 0: Communication Round 1: After TEST, request status is: %d\n", flag); + + /* Round 2 */ + MPI_Start(&request); + + flag = 0; + MPI_Request_get_status(request, &flag, &status); + fprintf(stderr, "Rank 0: Communication Round 2: After START, request status is: %d\n", flag); + + /* Fill the sending partitions and mark as ready as each is filled; note data is different than first round */ + for (i = 0; i < partitions; ++i) { + for (j = 0; j < COUNT; ++j) message[j+(COUNT*i)] = 10*(j+(COUNT*i)); + MPI_Pready(i, request); + flag = 0; + MPI_Request_get_status(request, &flag, &status); + fprintf(stderr, "Rank 0: Communication Round 2: After PREADY #%d, request status is: %d\n", i, flag); + } + + fprintf(stderr, "Sent in round 2: "); + for (i=0; i < COUNT * PARTITIONS; ++i) fprintf(stderr, " %d", message[i]); + fprintf(stderr, "\n"); + + flag = 0; + while (!flag) { + CHECK_RETVAL(MPI_Test(&request, &flag, MPI_STATUS_IGNORE)); + } + flag = 0; + MPI_Request_get_status(request, &flag, &status); + fprintf(stderr, "Rank 0: Communication Round 2: After TEST, request status is: %d\n", flag); + + MPI_Request_free(&request); + + } + else if (1 == myrank) { + /* Round 1 */ + CHECK_RETVAL(MPI_Precv_init(message, partitions, COUNT, MPI_INT, source, tag, MPI_COMM_WORLD , MPI_INFO_NULL , &request)); + CHECK_RETVAL(MPI_Start(&request)); + while (!flag) { + CHECK_RETVAL(MPI_Test(&request, &flag, MPI_STATUS_IGNORE)); + } + + fprintf(stderr, "Received in round 1: "); + for (i=0; i < COUNT * PARTITIONS; ++i) fprintf(stderr, " %d", message[i]); + fprintf(stderr, "\n"); + + /* Round 2 */ + CHECK_RETVAL(MPI_Start(&request)); + while (!flag) { + CHECK_RETVAL(MPI_Test(&request, &flag, MPI_STATUS_IGNORE)); + } + + MPI_Request_free(&request); + + fprintf(stderr, "Received in round 2: "); + for (i=0; i < COUNT * PARTITIONS; ++i) fprintf(stderr, " %d", message[i]); + fprintf(stderr, "\n"); + + /* all partitions received; check contents */ + for (i = 0; i < PARTITIONS*COUNT; ++i) { + if (message[i] != 10*i) { + fprintf(stderr, "ERROR: Contents received do not match contents sent (expected %d, found %d).\n",10*i,message[i]); + MPI_Abort(MPI_COMM_WORLD, 1); + } + } + } + + MPI_Barrier(MPI_COMM_WORLD); + if (0 == myrank) {TEST_RAN_TO_COMPLETION();} + + MPI_Finalize (); + + return 0; +} + diff --git a/partitioned-communication/test_wildcard0.c b/partitioned-communication/test_wildcard0.c new file mode 100644 index 0000000..6853dbc --- /dev/null +++ b/partitioned-communication/test_wildcard0.c @@ -0,0 +1,79 @@ +// Copyright 2024 National Technology & Engineering Solutions of Sandia, LLC +// (NTESS). Under the terms of Contract DE-NA0003525 with NTESS, the U.S. +// Government retains certain rights in this software. + +/* + * This test attempts to use MPI_ANY_SOURCE + * + * P. 118, line 28: Wildcards for source and tag are not allowed. + * + * Expected outcome: Error message + * + */ + +#include "test_common.h" + +#define PARTITIONS 8 +#define COUNT 5 + +int main(int argc, char *argv[]) { + + int message[PARTITIONS*COUNT]; + + MPI_Count partitions = PARTITIONS; + + int source = 0, dest = 1, tag = 1, flag = 0; + int myrank, i, j; + int provided; + + for (i = 0; i < PARTITIONS * COUNT; ++i) message[i] = 0; + + MPI_Request request; + MPI_Init_thread(&argc, &argv, MPI_THREAD_SERIALIZED, &provided); + if (provided < MPI_THREAD_SERIALIZED) MPI_Abort(MPI_COMM_WORLD , EXIT_FAILURE); MPI_Comm_rank(MPI_COMM_WORLD , &myrank); + + if (0 == myrank) { + + MPI_Psend_init(message, partitions, COUNT, MPI_INT, dest, tag, MPI_COMM_WORLD , MPI_INFO_NULL , &request); + MPI_Start(&request); + + /* Fill the sending partitions and mark as ready as each is filled */ + for (i = 0; i < partitions; ++i) { + for (j = 0; j < COUNT; ++j) message[j+(COUNT*i)] = j+(COUNT*i); + MPI_Pready(i, request); + } + + while (!flag) { + MPI_Test(&request, &flag, MPI_STATUS_IGNORE); + } + + MPI_Request_free(&request); + + } + else if (1 == myrank) { + /* This call should cause error because of MPI_ANY_SOURCE */ + CHECK_RETVAL(MPI_Precv_init(message, partitions, COUNT, MPI_INT, MPI_ANY_SOURCE, tag, MPI_COMM_WORLD , MPI_INFO_NULL , &request)); + CHECK_RETVAL(MPI_Start(&request)); + while (!flag) { + MPI_Test(&request, &flag, MPI_STATUS_IGNORE); + } + + MPI_Request_free(&request); + + /* all partitions received; check contents */ + for (i = 0; i < PARTITIONS*COUNT; ++i) { + if (message[i] != i) { + fprintf(stderr, "ERROR: Contents received do not match contents sent (expected %d, found %d).\n",i,message[i]); + MPI_Abort(MPI_COMM_WORLD, 1); + } + } + } + + MPI_Barrier(MPI_COMM_WORLD); + if (0 == myrank) {TEST_RAN_TO_COMPLETION();} + + MPI_Finalize(); + + return 0; +} + diff --git a/partitioned-communication/test_wildcard1.c b/partitioned-communication/test_wildcard1.c new file mode 100644 index 0000000..52bbd58 --- /dev/null +++ b/partitioned-communication/test_wildcard1.c @@ -0,0 +1,79 @@ +// Copyright 2024 National Technology & Engineering Solutions of Sandia, LLC +// (NTESS). Under the terms of Contract DE-NA0003525 with NTESS, the U.S. +// Government retains certain rights in this software. + +/* + * This test attempts to use MPI_ANY_TAG + * + * P. 118, line 28: Wildcards for source and tag are not allowed. + * + * Expected outcome: Error message + * + */ + +#include "test_common.h" + +#define PARTITIONS 8 +#define COUNT 5 + +int main(int argc, char *argv[]) { + + int message[PARTITIONS*COUNT]; + + MPI_Count partitions = PARTITIONS; + + int source = 0, dest = 1, tag = 1, flag = 0; + int myrank, i, j; + int provided; + + for (i = 0; i < PARTITIONS * COUNT; ++i) message[i] = 0; + + MPI_Request request; + MPI_Init_thread(&argc, &argv, MPI_THREAD_SERIALIZED, &provided); + if (provided < MPI_THREAD_SERIALIZED) MPI_Abort(MPI_COMM_WORLD , EXIT_FAILURE); MPI_Comm_rank(MPI_COMM_WORLD , &myrank); + + if (0 == myrank) { + + MPI_Psend_init(message, partitions, COUNT, MPI_INT, dest, tag, MPI_COMM_WORLD , MPI_INFO_NULL , &request); + MPI_Start(&request); + + /* Fill the sending partitions and mark as ready as each is filled */ + for (i = 0; i < partitions; ++i) { + for (j = 0; j < COUNT; ++j) message[j+(COUNT*i)] = j+(COUNT*i); + MPI_Pready(i, request); + } + + while (!flag) { + MPI_Test(&request, &flag, MPI_STATUS_IGNORE); + } + + MPI_Request_free(&request); + + } + else if (1 == myrank) { + /* This call should cause error because of MPI_ANY_TAG */ + CHECK_RETVAL(MPI_Precv_init(message, partitions, COUNT, MPI_INT, source, MPI_ANY_TAG, MPI_COMM_WORLD , MPI_INFO_NULL , &request)); + CHECK_RETVAL(MPI_Start(&request)); + while (!flag) { + MPI_Test(&request, &flag, MPI_STATUS_IGNORE); + } + + MPI_Request_free(&request); + + /* all partitions received; check contents */ + for (i = 0; i < PARTITIONS*COUNT; ++i) { + if (message[i] != i) { + fprintf(stderr, "ERROR: Contents received do not match contents sent (expected %d, found %d).\n",i,message[i]); + MPI_Abort(MPI_COMM_WORLD, 1); + } + } + } + + MPI_Barrier(MPI_COMM_WORLD); + if (0 == myrank) {TEST_RAN_TO_COMPLETION();} + + MPI_Finalize(); + + return 0; +} + diff --git a/partitioned-communication/test_zerocount0.c b/partitioned-communication/test_zerocount0.c new file mode 100644 index 0000000..9e6dfcf --- /dev/null +++ b/partitioned-communication/test_zerocount0.c @@ -0,0 +1,79 @@ +// Copyright 2024 National Technology & Engineering Solutions of Sandia, LLC +// (NTESS). Under the terms of Contract DE-NA0003525 with NTESS, the U.S. +// Government retains certain rights in this software. + +/* + * In PSEND_INIT and PRECV_INIT, partitions and count can be zero. + * P. 116, line 19: In PSEND_INIT, partitions is defined as “number of partitions (non-negative integer)” + * P. 116, line 20: In PSEND_INIT, count is defined as “number of elements per partition (non-negative integer)”. + * Same for PRECV_INIT (p. 117, line 27 and p. 117, line 29) + * + * This test confirms nothing goes wrong when both PSEND_INIT and PRECV_INIT use 0 partitions with a > 0 count. + * + * Expected outcome: PASS + * + */ + +#include "test_common.h" + +#define PARTITIONS 8 +#define COUNT 5 + +int main(int argc, char *argv[]) { + + int message[PARTITIONS*COUNT]; + + MPI_Count partitions = PARTITIONS; + + MPI_Count comm_partitions = 0; // use 0 partitions for init + MPI_Count comm_count = COUNT; // use COUNT partitions for init + + int source = 0, dest = 1, tag = 1, flag = 0; + int myrank, i, j; + int provided; + + for (i = 0; i < PARTITIONS * COUNT; ++i) message[i] = 0; + + MPI_Request request; + MPI_Init_thread(&argc, &argv, MPI_THREAD_SERIALIZED, &provided); + if (provided < MPI_THREAD_SERIALIZED) MPI_Abort(MPI_COMM_WORLD , EXIT_FAILURE); + MPI_Comm_rank(MPI_COMM_WORLD , &myrank); + + if (0 == myrank) { + + CHECK_RETVAL(MPI_Psend_init(message, comm_partitions, comm_count, MPI_INT, dest, tag, MPI_COMM_WORLD , MPI_INFO_NULL , &request)); + CHECK_RETVAL(MPI_Start(&request)); + + /* Fill the sending partitions and mark as ready as each is filled */ + /* Because there are 0 partitions, Pready never gets called */ + for (i = 0; i < comm_partitions; ++i) { + for (j = 0; j < COUNT; ++j) message[j+(COUNT*i)] = j+(COUNT*i); + CHECK_RETVAL(MPI_Pready(i, request)); + } + + while (!flag) { + CHECK_RETVAL(MPI_Test(&request, &flag, MPI_STATUS_IGNORE)); + } + + MPI_Request_free(&request); + + } + else if (1 == myrank) { + CHECK_RETVAL(MPI_Precv_init(message, comm_partitions, comm_count, MPI_INT, source, tag, MPI_COMM_WORLD , MPI_INFO_NULL , &request)); + CHECK_RETVAL(MPI_Start(&request)); + while (!flag) { + CHECK_RETVAL(MPI_Test(&request, &flag, MPI_STATUS_IGNORE)); + } + + MPI_Request_free(&request); + + } + + MPI_Barrier(MPI_COMM_WORLD); + if (0 == myrank) {TEST_RAN_TO_COMPLETION();} + + MPI_Finalize(); + + return 0; +} + diff --git a/partitioned-communication/test_zerocount1.c b/partitioned-communication/test_zerocount1.c new file mode 100644 index 0000000..aff5327 --- /dev/null +++ b/partitioned-communication/test_zerocount1.c @@ -0,0 +1,79 @@ +// Copyright 2024 National Technology & Engineering Solutions of Sandia, LLC +// (NTESS). Under the terms of Contract DE-NA0003525 with NTESS, the U.S. +// Government retains certain rights in this software. + +/* + * In PSEND_INIT and PRECV_INIT, partitions and count can be zero. + * P. 116, line 19: In PSEND_INIT, partitions is defined as “number of partitions (non-negative integer)” + * P. 116, line 20: In PSEND_INIT, count is defined as “number of elements per partition (non-negative integer)”. + * Same for PRECV_INIT (p. 117, line 27 and p. 117, line 29) + * + * This test confirms nothing goes wrong when both PSEND_INIT and PRECV_INIT use > 0 partitions each with 0 count. + * + * Expected outcome: PASS + * + */ + +#include "test_common.h" + +#define PARTITIONS 8 +#define COUNT 5 + +int main(int argc, char *argv[]) { + + int message[PARTITIONS*COUNT]; + + MPI_Count partitions = PARTITIONS; + + MPI_Count comm_partitions = PARTITIONS; // use PARTITIONS partitions for init + MPI_Count comm_count = 0; // use 0 partitions for init + + int source = 0, dest = 1, tag = 1, flag = 0; + int myrank, i, j; + int provided; + + for (i = 0; i < PARTITIONS * COUNT; ++i) message[i] = 0; + + MPI_Request request; + MPI_Init_thread(&argc, &argv, MPI_THREAD_SERIALIZED, &provided); + if (provided < MPI_THREAD_SERIALIZED) MPI_Abort(MPI_COMM_WORLD , EXIT_FAILURE); + MPI_Comm_rank(MPI_COMM_WORLD , &myrank); + + if (0 == myrank) { + + CHECK_RETVAL(MPI_Psend_init(message, comm_partitions, comm_count, MPI_INT, dest, tag, MPI_COMM_WORLD , MPI_INFO_NULL , &request)); + CHECK_RETVAL(MPI_Start(&request)); + + /* Fill the sending partitions and mark as ready as each is filled */ + /* There are non-zero partitions, so the message buffer will get filled; however, the message generated by or after PREADY will have a 0 count payload */ + for (i = 0; i < comm_partitions; ++i) { + for (j = 0; j < COUNT; ++j) message[j+(COUNT*i)] = j+(COUNT*i); + CHECK_RETVAL(MPI_Pready(i, request)); + } + + while (!flag) { + CHECK_RETVAL(MPI_Test(&request, &flag, MPI_STATUS_IGNORE)); + } + + MPI_Request_free(&request); + + } + else if (1 == myrank) { + CHECK_RETVAL(MPI_Precv_init(message, comm_partitions, comm_count, MPI_INT, source, tag, MPI_COMM_WORLD , MPI_INFO_NULL , &request)); + CHECK_RETVAL(MPI_Start(&request)); + while (!flag) { + CHECK_RETVAL(MPI_Test(&request, &flag, MPI_STATUS_IGNORE)); + } + + MPI_Request_free(&request); + + } + + MPI_Barrier(MPI_COMM_WORLD); + if (0 == myrank) {TEST_RAN_TO_COMPLETION();} + + MPI_Finalize(); + + return 0; +} +