diff --git a/notebooks/figures/fig_mpi.svg b/notebooks/figures/fig_mpi.svg index 5442ae9..582e86a 100644 --- a/notebooks/figures/fig_mpi.svg +++ b/notebooks/figures/fig_mpi.svg @@ -412,6 +412,37 @@ id="path4221-3" inkscape:connector-curvature="0" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + rank 0 + rank 1 + + + + + + + + + + rank 2 + msg + + MPI_Send + MPI_Recv + diff --git a/notebooks/julia_mpi.ipynb b/notebooks/julia_mpi.ipynb index 16c5ad3..c613b80 100644 --- a/notebooks/julia_mpi.ipynb +++ b/notebooks/julia_mpi.ipynb @@ -131,7 +131,7 @@ }, { "cell_type": "markdown", - "id": "1f6cdf37", + "id": "9db130db", "metadata": {}, "source": [ "## Minimal MPI program" @@ -139,7 +139,7 @@ }, { "cell_type": "markdown", - "id": "d0e8add1", + "id": "8f15cfda", "metadata": {}, "source": [ "\n", @@ -177,7 +177,7 @@ }, { "cell_type": "markdown", - "id": "95d2f1fe", + "id": "87dfb768", "metadata": {}, "source": [ "### An incorrect MPI program\n", @@ -194,7 +194,7 @@ }, { "cell_type": "markdown", - "id": "20009d70", + "id": "65de4419", "metadata": {}, "source": [ "### Solving the issue" @@ -202,7 +202,7 @@ }, { "cell_type": "markdown", - "id": "ae3d540c", + "id": "4a8ebeff", "metadata": {}, "source": [ "Premature finalization of a program is done with `MPI.Abort`.\n", @@ -222,7 +222,7 @@ }, { "cell_type": "markdown", - "id": "33fa3d33", + "id": "fa7145bc", "metadata": {}, "source": [ "### Read the docs\n", @@ -232,63 +232,10 @@ }, { "cell_type": "code", - "execution_count": 23, - "id": "7cef640d", + "execution_count": null, + "id": "a74c7c72", "metadata": {}, - "outputs": [ - { - "data": { - "text/latex": [ - "\\begin{verbatim}\n", - "Finalize()\n", - "\\end{verbatim}\n", - "Marks MPI state for cleanup. This should be called after \\href{@ref}{\\texttt{MPI.Init}}, and can be called at most once. No further MPI calls (other than \\href{@ref}{\\texttt{Initialized}} or \\href{@ref}{\\texttt{Finalized}}) should be made after it is called.\n", - "\n", - "\\href{@ref}{\\texttt{MPI.Init}} will automatically insert a hook to call this function when Julia exits, if it hasn't already been called.\n", - "\n", - "\\section{External links}\n", - "\\begin{itemize}\n", - "\\item \\texttt{MPI\\_Finalize} man page: \\href{https://www.open-mpi.org/doc/current/man3/MPI_Finalize.3.php}{OpenMPI}, \\href{https://www.mpich.org/static/docs/latest/www3/MPI_Finalize.html}{MPICH}\n", - "\n", - "\\end{itemize}\n" - ], - "text/markdown": [ - "```\n", - "Finalize()\n", - "```\n", - "\n", - "Marks MPI state for cleanup. This should be called after [`MPI.Init`](@ref), and can be called at most once. No further MPI calls (other than [`Initialized`](@ref) or [`Finalized`](@ref)) should be made after it is called.\n", - "\n", - "[`MPI.Init`](@ref) will automatically insert a hook to call this function when Julia exits, if it hasn't already been called.\n", - "\n", - "# External links\n", - "\n", - " * `MPI_Finalize` man page: [OpenMPI](https://www.open-mpi.org/doc/current/man3/MPI_Finalize.3.php), [MPICH](https://www.mpich.org/static/docs/latest/www3/MPI_Finalize.html)\n" - ], - "text/plain": [ - "\u001b[36m Finalize()\u001b[39m\n", - "\n", - " Marks MPI state for cleanup. This should be called after \u001b[36mMPI.Init\u001b[39m, and can\n", - " be called at most once. No further MPI calls (other than \u001b[36mInitialized\u001b[39m or\n", - " \u001b[36mFinalized\u001b[39m) should be made after it is called.\n", - "\n", - " \u001b[36mMPI.Init\u001b[39m will automatically insert a hook to call this function when Julia\n", - " exits, if it hasn't already been called.\n", - "\n", - "\u001b[1m External links\u001b[22m\n", - "\u001b[1m ≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡\u001b[22m\n", - "\n", - " • \u001b[36mMPI_Finalize\u001b[39m man page: OpenMPI\n", - " (https://www.open-mpi.org/doc/current/man3/MPI_Finalize.3.php),\n", - " MPICH\n", - " (https://www.mpich.org/static/docs/latest/www3/MPI_Finalize.html)" - ] - }, - "execution_count": 23, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "? MPI.Finalize" ] @@ -305,21 +252,10 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": null, "id": "96f7c14e", "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "MPI.ThreadLevel(2)" - ] - }, - "execution_count": 12, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "using MPI\n", "MPI.Init()" @@ -327,7 +263,7 @@ }, { "cell_type": "markdown", - "id": "11fd96a4", + "id": "fe202985", "metadata": {}, "source": [ " Current rank (process) id" @@ -335,21 +271,10 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": null, "id": "bd8232f5", "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "0" - ] - }, - "execution_count": 13, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "comm = MPI.COMM_WORLD\n", "rank = MPI.Comm_rank(comm)" @@ -357,7 +282,7 @@ }, { "cell_type": "markdown", - "id": "92ed5b7d", + "id": "dd40d1dc", "metadata": {}, "source": [ "Number of available processes" @@ -365,28 +290,17 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": null, "id": "0befa408", "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "1" - ] - }, - "execution_count": 14, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "nranks = MPI.Comm_size(comm)" ] }, { "cell_type": "markdown", - "id": "2b689794", + "id": "d1eeaf81", "metadata": {}, "source": [ "Name of the current host" @@ -394,21 +308,10 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": null, "id": "ff01adcf", "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "\"dell-5570-vu\"" - ] - }, - "execution_count": 11, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "MPI.Get_processor_name()" ] @@ -433,18 +336,10 @@ }, { "cell_type": "code", - "execution_count": 25, + "execution_count": null, "id": "a154b55e", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Hello from dell-5570-vu, I am process 0 of 1 processes!\n" - ] - } - ], + "outputs": [], "source": [ "using MPI\n", "MPI.Init()\n", @@ -540,7 +435,7 @@ }, { "cell_type": "code", - "execution_count": 29, + "execution_count": null, "id": "e03f35c5", "metadata": {}, "outputs": [], @@ -568,21 +463,10 @@ }, { "cell_type": "code", - "execution_count": 30, + "execution_count": null, "id": "dbe654dc", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Hello, I am process 0 of 4 processes!\n", - "Hello, I am process 1 of 4 processes!\n", - "Hello, I am process 2 of 4 processes!\n", - "Hello, I am process 3 of 4 processes!\n" - ] - } - ], + "outputs": [], "source": [ "using MPI\n", "run(`$(mpiexec()) -np 4 julia --project=. $filename`);" @@ -620,21 +504,10 @@ }, { "cell_type": "code", - "execution_count": 40, + "execution_count": null, "id": "359e569b", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Hello, I am process 0 of 4 processes!\n", - "Hello, I am process 1 of 4 processes!\n", - "Hello, I am process 2 of 4 processes!\n", - "Hello, I am process 3 of 4 processes!\n" - ] - } - ], + "outputs": [], "source": [ "code = quote\n", " using MPI\n", @@ -650,7 +523,7 @@ }, { "cell_type": "markdown", - "id": "b58f7638", + "id": "5ec1c52a", "metadata": {}, "source": [ "### Data availability" @@ -666,8 +539,8 @@ }, { "cell_type": "code", - "execution_count": 49, - "id": "14f1f901", + "execution_count": null, + "id": "16bb608a", "metadata": {}, "outputs": [], "source": [ @@ -682,7 +555,7 @@ }, { "cell_type": "markdown", - "id": "2db17171", + "id": "4b455f98", "metadata": {}, "source": [ "So, the full MPI program needs to be in the source file passed to Julia or the quote block. In practice, long MPI programms are written as Julia packages using several files, which are then loaded by each MPI process. " @@ -690,18 +563,10 @@ }, { "cell_type": "code", - "execution_count": 39, - "id": "cff5476a", + "execution_count": null, + "id": "b816659a", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Hi there!Hi there!Hi there!" - ] - } - ], + "outputs": [], "source": [ "code = quote\n", " foo() = print(\"Hi there!\")\n", @@ -714,7 +579,7 @@ }, { "cell_type": "markdown", - "id": "5e905f73", + "id": "a37daef2", "metadata": {}, "source": [ "## Point-to-point communication\n", @@ -722,7 +587,8 @@ "MPI provides point-to-point communication directives for arbitrary communication between processes. Point-to-point communications are two-sided: there is a sender and a receiver. Here, we will discuss these basic directives:\n", "\n", "- `MPI_Send`, and `MPI_Recv!` (*blocking directives*)\n", - "- `MPI_Isend`, and `MPI_Irecv!` (*non-blocking directives, aka incomplete directives*)" + "- `MPI_Isend`, and `MPI_Irecv!` (*non-blocking directives, aka incomplete directives*)\n", + "- `MPI_Bsend`, `MPI_Ssend`, and `MPI_Rsend` (*advanced communication modes*)" ] }, { @@ -732,7 +598,7 @@ } }, "cell_type": "markdown", - "id": "771a8b5d", + "id": "14674ca9", "metadata": {}, "source": [ "
\n", @@ -742,10 +608,10 @@ }, { "cell_type": "markdown", - "id": "001f022b", + "id": "ead20fc0", "metadata": {}, "source": [ - "### Blocking send/receive\n", + "## Blocking send and receive\n", "\n", "In Julia:\n", "\n", @@ -772,7 +638,7 @@ }, { "cell_type": "markdown", - "id": "19b6c4e1", + "id": "be4fae1c", "metadata": {}, "source": [ "### Example" @@ -780,7 +646,7 @@ }, { "cell_type": "markdown", - "id": "334be457", + "id": "c2301992", "metadata": {}, "source": [ "Send 5 integers from rank 2 to rank 3." @@ -788,18 +654,10 @@ }, { "cell_type": "code", - "execution_count": 54, - "id": "4fffb94a", + "execution_count": null, + "id": "61fe5db3", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "rcvbuf = [1, 2, 3, 5, 8]\n" - ] - } - ], + "outputs": [], "source": [ "code = quote\n", " using MPI\n", @@ -821,7 +679,7 @@ }, { "cell_type": "markdown", - "id": "29340e48", + "id": "ee680aa5", "metadata": {}, "source": [ "### Any source, any tag\n", @@ -841,7 +699,7 @@ }, { "cell_type": "markdown", - "id": "6c8af148", + "id": "1f0cb6c8", "metadata": {}, "source": [ "### Example" @@ -849,7 +707,7 @@ }, { "cell_type": "markdown", - "id": "f8806fb6", + "id": "2add5065", "metadata": {}, "source": [ "Send 5 integers from rank 2 to rank 3." @@ -857,18 +715,10 @@ }, { "cell_type": "code", - "execution_count": 61, - "id": "96c00b8a", + "execution_count": null, + "id": "ccdf660a", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "rcvbuf = [1, 2, 3, 5, 8]\n" - ] - } - ], + "outputs": [], "source": [ "code = quote\n", " using MPI\n", @@ -892,7 +742,7 @@ }, { "cell_type": "markdown", - "id": "b4f838cc", + "id": "7833da72", "metadata": {}, "source": [ "### Who was the sender? Which was the tag?\n", @@ -911,20 +761,10 @@ }, { "cell_type": "code", - "execution_count": 67, - "id": "0faa96df", + "execution_count": null, + "id": "2f037032", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "rcvbuf = [1, 2, 3, 5, 8]\n", - "status.source = 2\n", - "status.tag = 0\n" - ] - } - ], + "outputs": [], "source": [ "code = quote\n", " using MPI\n", @@ -950,7 +790,7 @@ }, { "cell_type": "markdown", - "id": "d519b6f0", + "id": "590bc407", "metadata": {}, "source": [ "### Which is the incoming message size?\n", @@ -959,26 +799,19 @@ "\n", "```julia\n", "status = MPI.Probe(comm,MPI.Status; source, tag)\n", - "```\n", - "```C\n", - "int MPI_Probe(int source, int tag, MPI_Comm comm, MPI_Status *status)\n", - "```\n", - "\n", - "We can get the message size from the status object using function `MPI_Get_count`. We can also get the source and tag from the status object as shown before.\n", - "\n", - "```julia\n", "count = MPI.Get_count(status, T)\n", "```\n", "```C\n", + "int MPI_Probe(int source, int tag, MPI_Comm comm, MPI_Status *status)\n", "int MPI_Get_count(const MPI_Status *status, MPI_Datatype datatype, int *count)\n", "```\n", "\n", - "Note that one needs to specify the data type." + "We can get the message size from the status object using function `MPI_Get_count`. We can also get the source and tag from the status object as shown before." ] }, { "cell_type": "markdown", - "id": "56896b06", + "id": "517d052e", "metadata": {}, "source": [ "### Example" @@ -986,19 +819,10 @@ }, { "cell_type": "code", - "execution_count": 73, - "id": "b20f565a", + "execution_count": null, + "id": "1b54af36", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "I am about to receive 5 integers.\n", - "rcvbuf = [1, 2, 3, 5, 8]\n" - ] - } - ], + "outputs": [], "source": [ "code = quote\n", " using MPI\n", @@ -1025,7 +849,7 @@ }, { "cell_type": "markdown", - "id": "2d6938ad", + "id": "04bdb01f", "metadata": {}, "source": [ "### Blocking semantics\n", @@ -1040,18 +864,10 @@ }, { "cell_type": "code", - "execution_count": 76, - "id": "497fe2df", + "execution_count": null, + "id": "2299bf78", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "rcvbuf = [1, 2, 3, 5, 8]\n" - ] - } - ], + "outputs": [], "source": [ "code = quote\n", " using MPI\n", @@ -1075,7 +891,7 @@ }, { "cell_type": "markdown", - "id": "5619caba", + "id": "245893cf", "metadata": {}, "source": [ "However:\n", @@ -1086,31 +902,20 @@ }, { "cell_type": "markdown", - "id": "5f739652", + "id": "59fcac63", "metadata": {}, "source": [ "### Incorrect program\n", "\n", - "The following program will or will not work depending whether the underlying implementation uses buffering. In my case, it works with `n=1` below, but leads to a dead lock when `n=10000`. The MPI implementation might decide to buffer or not depending on the message size." + "The following program will or will not work depending whether the underlying implementation uses buffering. On my laptop, it works with `n=1`, but leads to a dead lock when `n=10000`. The MPI implementation decided to buffer or not depending on the message size." ] }, { "cell_type": "code", - "execution_count": 93, - "id": "e8bedc57", + "execution_count": null, + "id": "42fb8089", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "(rcvbuf[1], rank) = (0, 0)\n", - "(rcvbuf[1], rank) = (0, 1)\n", - "(rcvbuf[1], rank) = (3, 2)\n", - "(rcvbuf[1], rank) = (2, 3)\n" - ] - } - ], + "outputs": [], "source": [ "code = quote\n", " using MPI\n", @@ -1135,7 +940,7 @@ }, { "cell_type": "markdown", - "id": "a6f47642", + "id": "8b7eeec2", "metadata": {}, "source": [ "### Correct program\n", @@ -1145,21 +950,10 @@ }, { "cell_type": "code", - "execution_count": 92, - "id": "276e40c5", + "execution_count": null, + "id": "94c8d5e9", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "(rcvbuf[1], rank) = (0, 0)\n", - "(rcvbuf[1], rank) = (0, 1)\n", - "(rcvbuf[1], rank) = (3, 2)\n", - "(rcvbuf[1], rank) = (2, 3)\n" - ] - } - ], + "outputs": [], "source": [ "code = quote\n", " using MPI\n", @@ -1184,7 +978,7 @@ }, { "cell_type": "markdown", - "id": "0890f627", + "id": "2b37ed85", "metadata": {}, "source": [ "Another solution is to use `MPI_Sendrecv`." @@ -1192,21 +986,10 @@ }, { "cell_type": "code", - "execution_count": 94, - "id": "d648e1cf", + "execution_count": null, + "id": "9c0c84f0", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "(rcvbuf[1], rank) = (0, 1)\n", - "(rcvbuf[1], rank) = (0, 0)\n", - "(rcvbuf[1], rank) = (3, 2)\n", - "(rcvbuf[1], rank) = (2, 3)\n" - ] - } - ], + "outputs": [], "source": [ "code = quote\n", " using MPI\n", @@ -1227,6 +1010,270 @@ "run(`$(mpiexec()) -np 4 julia --project=. -e $code`);" ] }, + { + "cell_type": "markdown", + "id": "4fca501c", + "metadata": {}, + "source": [ + "## Communication modes\n", + "\n", + "In all cases, it is safe to reuse the send buffer once the corresponding send returns. I.e., all the following sends are *complete* MPI operations. However, there are some important differences.\n", + "\n", + "### Standard\n", + "\n", + "* `MPI_Send` \n", + "* Programmer cannot make any assumptions whether the message is buffered or synchronous.\n", + "* This is up to the system.\n", + "\n", + "### Buffered\n", + "\n", + "* `MPI_Bsend`\n", + "* Programmer provides additional internal buffer space with function `MPI_Buffer_attach`.\n", + "* `MPI_Bsend` completes when message is copied into a local buffer.\n", + "* Erroneous if buffer space is insufficient.\n", + "\n", + "### Synchronous\n", + "\n", + "* `MPI_Ssend`\n", + "* The send will only return if a matching receive was posted.\n", + "* No buffering → easy to get deadlocks\n", + "* It can be started whether or not a matching receive was posted.\n", + "\n", + "### Ready\n", + "\n", + "* `MPI_Rsend`\n", + "* It may be started only if the matching receive is already posted.\n", + "* Erroneous if there is no matching receive yet.\n", + "* Otherwise, same as an `MPI_Ssend`.\n", + "\n", + "\n", + " All these send types are matched with a `MPI_Recv`. I.e., there is no `MPI_Brecv`, `MPI_Srecv`, `MPI_Rrecv`. For further information about the communication modes, refer to [this section](https://www.mpi-forum.org/docs/mpi-2.2/mpi22-report/node53.htm) of the MPI standard.\n", + " \n", + " \n", + "
\n", + "Note: `MPI_Bsend`, `MPI_Ssend`, and `MPI_Rsend` are not exposed in the Julia bindings via a high-level interface like for `MPI.Send`, but they can be accessed using the low-level bindings in the submodule `MPI.API` (not shown in this notebook).\n", + "
\n", + " \n", + " \n" + ] + }, + { + "cell_type": "markdown", + "id": "7a935b14", + "metadata": {}, + "source": [ + "## Non-blocking send and receive\n", + "\n", + "- They return immediately.\n", + "- The send and receive is not completed when the function returns. I.e. they are *incomplete* operations.\n", + "- `MPI_Wait` used to wait for completion of the send and/or receive.\n", + "\n", + "- Used to overlap communication and computation (\"latency-hiding\").\n", + "\n", + "\n", + "```julia\n", + "request = MPI.Isend(sndbuf, comm; dest, tag)\n", + "request = MPI.Irecv!(rcvbuf, comm; source, tag)\n", + "```\n", + "\n", + "```C\n", + "int MPI_Isend(const void *sndbuf, int count, MPI_Datatype datatype, int dest,\n", + " int tag, MPI_Comm comm, MPI_Request *request)\n", + "int MPI_Irecv(void *rcvbuf, int count, MPI_Datatype datatype,\n", + " int source, int tag, MPI_Comm comm, MPI_Request *request)\n", + "\n", + "```" + ] + }, + { + "cell_type": "markdown", + "id": "85903e61", + "metadata": {}, + "source": [ + "### Example\n", + "\n", + "Send 5 integers from rank 2 to rank 3. Both ranks do some work while messages are being communicated." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "42284c69", + "metadata": {}, + "outputs": [], + "source": [ + "code = quote\n", + " using MPI\n", + " MPI.Init()\n", + " work() = sum(rand(1000))\n", + " comm = MPI.COMM_WORLD\n", + " rank = MPI.Comm_rank(comm)\n", + " if rank == 2\n", + " sndbuf = [1,2,3,5,8]\n", + " request = MPI.Isend(sndbuf, comm; dest=3, tag=0)\n", + " work()\n", + " MPI.Wait(request)\n", + " end\n", + " if rank == 3\n", + " rcvbuf = zeros(Int,5)\n", + " request = MPI.Irecv!(rcvbuf, comm; source=2, tag=0)\n", + " work()\n", + " MPI.Wait(request)\n", + " @show rcvbuf\n", + " end\n", + "end\n", + "run(`$(mpiexec()) -np 4 julia --project=. -e $code`);" + ] + }, + { + "cell_type": "markdown", + "id": "0180c576", + "metadata": {}, + "source": [ + "### Incorrect program\n", + "\n", + "This program in incorrect both on the send and the receive side.\n", + "\n", + "- One needs to wait for completion before reseting the send buffer\n", + "- One needs to wait for completion before using the receive buffer\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "52991807", + "metadata": {}, + "outputs": [], + "source": [ + "code = quote\n", + " using MPI\n", + " MPI.Init()\n", + " comm = MPI.COMM_WORLD\n", + " rank = MPI.Comm_rank(comm)\n", + " if rank == 2\n", + " sndbuf = [1,2,3,5,8]\n", + " request = MPI.Isend(sndbuf, comm; dest=3, tag=0)\n", + " sndbuf .= 10 # We cannot set the sndbuf before MPI.Wait.\n", + " MPI.Wait(request)\n", + " end\n", + " if rank == 3\n", + " rcvbuf = zeros(Int,5)\n", + " request = MPI.Irecv!(rcvbuf, comm; source=2, tag=0)\n", + " @show rcvbuf # Not guaranteed to have the correct value.\n", + " MPI.Wait(request)\n", + " end\n", + "end\n", + "run(`$(mpiexec()) -np 4 julia --project=. -e $code`);" + ] + }, + { + "cell_type": "markdown", + "id": "4cc264f1", + "metadata": {}, + "source": [ + "### Which is the incoming message size?\n", + "\n", + "If we use `MPI_Probe` we miss the opportunity to do local work before a send is started." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "03b11966", + "metadata": {}, + "outputs": [], + "source": [ + "code = quote\n", + " using MPI\n", + " MPI.Init()\n", + " work() = sum(rand(1000))\n", + " comm = MPI.COMM_WORLD\n", + " rank = MPI.Comm_rank(comm)\n", + " if rank == 2\n", + " sleep(5) # Sleep 5 seconds\n", + " sndbuf = [1,2,3,5,8]\n", + " request = MPI.Isend(sndbuf, comm; dest=3, tag=0)\n", + " MPI.Wait(request)\n", + " end\n", + " if rank == 3\n", + " # We are going to wait here for about 5 seconds\n", + " # Missing the opportunity to do some useful work\n", + " status = MPI.Probe(comm,MPI.Status; source=2, tag=0)\n", + " count = MPI.Get_count(status,Int)\n", + " rcvbuf = zeros(Int,count)\n", + " request = MPI.Irecv!(rcvbuf, comm; source=2, tag=0)\n", + " work()\n", + " MPI.Wait(request)\n", + " @show rcvbuf\n", + " end\n", + "end\n", + "run(`$(mpiexec()) -np 4 julia --project=. -e $code`);" + ] + }, + { + "cell_type": "markdown", + "id": "6761b4fa", + "metadata": {}, + "source": [ + "We can fix this using an `MPI_Iprobe`. It allows us to check for incoming messages without blocking.\n", + "\n", + "```julia\n", + "ismsg, status = MPI.Iprobe(comm, MPI.Status; source, tag)\n", + "```\n", + "```C\n", + "int MPI_Iprobe(int source, int tag, MPI_Comm comm, int *flag,\n", + " MPI_Status *status)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "75fdb2b1", + "metadata": {}, + "outputs": [], + "source": [ + "code = quote\n", + " using MPI\n", + " MPI.Init()\n", + " work() = sum(rand(1000))\n", + " comm = MPI.COMM_WORLD\n", + " rank = MPI.Comm_rank(comm)\n", + " if rank == 2\n", + " sleep(5) # Sleep 5 seconds\n", + " sndbuf = [1,2,3,5,8]\n", + " request = MPI.Isend(sndbuf, comm; dest=3, tag=0)\n", + " MPI.Wait(request)\n", + " end\n", + " if rank == 3\n", + " while true\n", + " ismsg, status = MPI.Iprobe(comm, MPI.Status; source=2, tag=0)\n", + " if ismsg\n", + " count = MPI.Get_count(status,Int)\n", + " rcvbuf = zeros(Int,count)\n", + " reqrcv = MPI.Irecv!(rcvbuf, comm; source=2, tag=0)\n", + " work()\n", + " MPI.Wait(reqrcv)\n", + " @show rcvbuf\n", + " break\n", + " end\n", + " work() # Do work while waiting for an incoming message.\n", + " end\n", + " end\n", + "end\n", + "run(`$(mpiexec()) -np 4 julia --project=. -e $code`);" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c168ca8e", + "metadata": {}, + "outputs": [], + "source": [ + "? MPI.Iprobe" + ] + }, { "cell_type": "markdown", "id": "6caa8d74", @@ -1285,21 +1332,10 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": null, "id": "1f8a70c6", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "I am sending 20\n", - "I am sending 30\n", - "I am sending 40\n", - "I have received: [20, 30, 40]\n" - ] - } - ], + "outputs": [], "source": [ "code = quote\n", " using MPI; MPI.Init()\n", @@ -1344,21 +1380,10 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": null, "id": "5ea413fd", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "I am sending: [20, 30, 40]\n", - "I have received: 20\n", - "I have received: 30I have received: 40\n", - "\n" - ] - } - ], + "outputs": [], "source": [ "code = quote\n", " using MPI; MPI.Init()\n", @@ -1406,21 +1431,10 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": null, "id": "4de15781", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "I am sending: 20\n", - "I have received: 20\n", - "I have received: 20\n", - "I have received: 20\n" - ] - } - ], + "outputs": [], "source": [ "code = quote\n", " using MPI; MPI.Init()\n", @@ -1449,20 +1463,10 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": null, "id": "ff61f64a", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Done!\n", - "Done!\n", - "Done!\n" - ] - } - ], + "outputs": [], "source": [ "code = quote\n", " using MPI; MPI.Init()\n", @@ -1828,15 +1832,15 @@ ], "metadata": { "kernelspec": { - "display_name": "Julia 1.9.0", + "display_name": "Julia 1.10.0", "language": "julia", - "name": "julia-1.9" + "name": "julia-1.10" }, "language_info": { "file_extension": ".jl", "mimetype": "application/julia", "name": "julia", - "version": "1.9.0" + "version": "1.10.0" } }, "nbformat": 4,