diff --git a/dev/.documenter-siteinfo.json b/dev/.documenter-siteinfo.json index b1d18ca..3a98eb7 100644 --- a/dev/.documenter-siteinfo.json +++ b/dev/.documenter-siteinfo.json @@ -1 +1 @@ -{"documenter":{"julia_version":"1.10.4","generation_timestamp":"2024-08-20T13:32:54","documenter_version":"1.5.0"}} \ No newline at end of file +{"documenter":{"julia_version":"1.10.4","generation_timestamp":"2024-08-22T08:36:00","documenter_version":"1.6.0"}} \ No newline at end of file diff --git a/dev/LEQ/index.html b/dev/LEQ/index.html index a1469f7..a35b589 100644 --- a/dev/LEQ/index.html +++ b/dev/LEQ/index.html @@ -14,4 +14,4 @@ var myIframe = document.getElementById("notebook"); iFrameResize({log:true}, myIframe); }); - + diff --git a/dev/asp/index.html b/dev/asp/index.html index 3138083..e60e04d 100644 --- a/dev/asp/index.html +++ b/dev/asp/index.html @@ -14,4 +14,4 @@ var myIframe = document.getElementById("notebook"); iFrameResize({log:true}, myIframe); }); - + diff --git a/dev/assets/documenter.js b/dev/assets/documenter.js index b2bdd43..82252a1 100644 --- a/dev/assets/documenter.js +++ b/dev/assets/documenter.js @@ -77,30 +77,35 @@ require(['jquery'], function($) { let timer = 0; var isExpanded = true; -$(document).on("click", ".docstring header", function () { - let articleToggleTitle = "Expand docstring"; +$(document).on( + "click", + ".docstring .docstring-article-toggle-button", + function () { + let articleToggleTitle = "Expand docstring"; + const parent = $(this).parent(); - debounce(() => { - if ($(this).siblings("section").is(":visible")) { - $(this) - .find(".docstring-article-toggle-button") - .removeClass("fa-chevron-down") - .addClass("fa-chevron-right"); - } else { - $(this) - .find(".docstring-article-toggle-button") - .removeClass("fa-chevron-right") - .addClass("fa-chevron-down"); + debounce(() => { + if (parent.siblings("section").is(":visible")) { + parent + .find("a.docstring-article-toggle-button") + .removeClass("fa-chevron-down") + .addClass("fa-chevron-right"); + } else { + parent + .find("a.docstring-article-toggle-button") + .removeClass("fa-chevron-right") + .addClass("fa-chevron-down"); - articleToggleTitle = "Collapse docstring"; - } + articleToggleTitle = "Collapse docstring"; + } - $(this) - .find(".docstring-article-toggle-button") - .prop("title", articleToggleTitle); - $(this).siblings("section").slideToggle(); - }); -}); + parent + .children(".docstring-article-toggle-button") + .prop("title", articleToggleTitle); + parent.siblings("section").slideToggle(); + }); + } +); $(document).on("click", ".docs-article-toggle-button", function (event) { let articleToggleTitle = "Expand docstring"; @@ -110,7 +115,7 @@ $(document).on("click", ".docs-article-toggle-button", function (event) { debounce(() => { if (isExpanded) { $(this).removeClass("fa-chevron-up").addClass("fa-chevron-down"); - $(".docstring-article-toggle-button") + $("a.docstring-article-toggle-button") .removeClass("fa-chevron-down") .addClass("fa-chevron-right"); @@ -119,7 +124,7 @@ $(document).on("click", ".docs-article-toggle-button", function (event) { $(".docstring section").slideUp(animationSpeed); } else { $(this).removeClass("fa-chevron-down").addClass("fa-chevron-up"); - $(".docstring-article-toggle-button") + $("a.docstring-article-toggle-button") .removeClass("fa-chevron-right") .addClass("fa-chevron-down"); diff --git a/dev/getting_started_with_julia/index.html b/dev/getting_started_with_julia/index.html index 6887f85..b0e71f2 100644 --- a/dev/getting_started_with_julia/index.html +++ b/dev/getting_started_with_julia/index.html @@ -13,4 +13,4 @@ julia> DataFrame(a=[1,2],b=[3,4])

You should get an error or a BenchmarkTools = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf" DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" MPI = "da04e1cc-30fd-572f-bb4f-1f8673147195"

Copy the contents of previous code block into a file called Project.toml and place it in an empty folder named newproject. It is important that the file is named Project.toml. You can create a new folder from the REPL with

julia> mkdir("newproject")

To install all the packages registered in this file you need to activate the folder containing your Project.toml file

(@v1.10) pkg> activate newproject

and then instantiating it

(newproject) pkg> instantiate

The instantiate command will download and install all listed packages and their dependencies in just one click.

Getting help in package mode

You can get help about a particular package operator by writing help in front of it

(@v1.10) pkg> help activate

You can get an overview of all package commands by typing help alone

(@v1.10) pkg> help

Package operations in Julia code

In some situations it is required to use package commands in Julia code, e.g., to automatize installation and deployment of Julia applications. This can be done using the Pkg package. For instance

julia> using Pkg
-julia> Pkg.status()

is equivalent to calling status in package mode.

(@v1.10) pkg> status

Conclusion

We have learned the basics of how to work with Julia. If you want to further dig into the topics we have covered here, you can take a look at the following links:

+julia> Pkg.status()

is equivalent to calling status in package mode.

(@v1.10) pkg> status

Conclusion

We have learned the basics of how to work with Julia. If you want to further dig into the topics we have covered here, you can take a look at the following links:

diff --git a/dev/index.html b/dev/index.html index cc313ef..839a46e 100644 --- a/dev/index.html +++ b/dev/index.html @@ -2,4 +2,4 @@ Home · XM_40017

Programming Large-Scale Parallel Systems (XM_40017)

Welcome to the interactive lecture notes of the Programming Large-Scale Parallel Systems course at VU Amsterdam!

What

This page contains part of the course material of the Programming Large-Scale Parallel Systems course at VU Amsterdam. We provide several lecture notes in jupyter notebook format, which will help you to learn how to design, analyze, and program parallel algorithms on multi-node computing systems. Further information about the course is found in the study guide (click here) and our Canvas page (for registered students).

Note

Material will be added incrementally to the website as the course advances.

Warning

This page will eventually contain only a part of the course material. The rest will be available on Canvas. In particular, the material in this public webpage does not fully cover all topics in the final exam.

How to use this page

You have two main ways of studying the notebooks:

  • Download the notebooks and run them locally on your computer (recommended). At each notebook page you will find a green box with links to download the notebook.
  • You also have the static version of the notebooks displayed in this webpage for quick reference.

How to run the notebooks locally

To run a notebook locally follow these steps:

  • Install Julia (if not done already). More information in Getting started.
  • Download the notebook.
  • Launch Julia. More information in Getting started.
  • Execute these commands in the Julia command line:
julia> using Pkg
 julia> Pkg.add("IJulia")
 julia> using IJulia
-julia> notebook()
  • These commands will open a jupyter in your web browser. Navigate in jupyter to the notebook file you have downloaded and open it.

Authors

This material is created by Francesc Verdugo with the help of Gelieza Kötterheinrich. Part of the notebooks are based on the course slides by Henri Bal.

License

All material on this page that is original to this course may be used under a CC BY 4.0 license.

Acknowledgment

This page was created with the support of the Faculty of Science of Vrije Universiteit Amsterdam in the framework of the project "Interactive lecture notes and exercises for the Programming Large-Scale Parallel Systems course" funded by the "Innovation budget BETA 2023 Studievoorschotmiddelen (SVM) towards Activated Blended Learning".

+julia> notebook()

Authors

This material is created by Francesc Verdugo with the help of Gelieza Kötterheinrich. Part of the notebooks are based on the course slides by Henri Bal.

License

All material on this page that is original to this course may be used under a CC BY 4.0 license.

Acknowledgment

This page was created with the support of the Faculty of Science of Vrije Universiteit Amsterdam in the framework of the project "Interactive lecture notes and exercises for the Programming Large-Scale Parallel Systems course" funded by the "Innovation budget BETA 2023 Studievoorschotmiddelen (SVM) towards Activated Blended Learning".

diff --git a/dev/jacobi_2D/index.html b/dev/jacobi_2D/index.html index afebdec..1968ebd 100644 --- a/dev/jacobi_2D/index.html +++ b/dev/jacobi_2D/index.html @@ -14,4 +14,4 @@ var myIframe = document.getElementById("notebook"); iFrameResize({log:true}, myIframe); }); - + diff --git a/dev/jacobi_method/index.html b/dev/jacobi_method/index.html index f296894..be23c0f 100644 --- a/dev/jacobi_method/index.html +++ b/dev/jacobi_method/index.html @@ -14,4 +14,4 @@ var myIframe = document.getElementById("notebook"); iFrameResize({log:true}, myIframe); }); - + diff --git a/dev/julia_async/index.html b/dev/julia_async/index.html index d210024..8d84f94 100644 --- a/dev/julia_async/index.html +++ b/dev/julia_async/index.html @@ -14,4 +14,4 @@ var myIframe = document.getElementById("notebook"); iFrameResize({log:true}, myIframe); }); - + diff --git a/dev/julia_basics/index.html b/dev/julia_basics/index.html index 035e57e..ded9cde 100644 --- a/dev/julia_basics/index.html +++ b/dev/julia_basics/index.html @@ -14,4 +14,4 @@ var myIframe = document.getElementById("notebook"); iFrameResize({log:true}, myIframe); }); - + diff --git a/dev/julia_distributed/index.html b/dev/julia_distributed/index.html index 8903487..cf99614 100644 --- a/dev/julia_distributed/index.html +++ b/dev/julia_distributed/index.html @@ -14,4 +14,4 @@ var myIframe = document.getElementById("notebook"); iFrameResize({log:true}, myIframe); }); - + diff --git a/dev/julia_intro/index.html b/dev/julia_intro/index.html index da8757b..9baa57e 100644 --- a/dev/julia_intro/index.html +++ b/dev/julia_intro/index.html @@ -14,4 +14,4 @@ var myIframe = document.getElementById("notebook"); iFrameResize({log:true}, myIframe); }); - + diff --git a/dev/julia_jacobi/index.html b/dev/julia_jacobi/index.html index bbd8c78..41567c7 100644 --- a/dev/julia_jacobi/index.html +++ b/dev/julia_jacobi/index.html @@ -14,4 +14,4 @@ var myIframe = document.getElementById("notebook"); iFrameResize({log:true}, myIframe); }); - + diff --git a/dev/julia_mpi.ipynb b/dev/julia_mpi.ipynb index d8042df..1340ce6 100644 --- a/dev/julia_mpi.ipynb +++ b/dev/julia_mpi.ipynb @@ -528,7 +528,7 @@ "metadata": {}, "source": [ "
\n", - "Note: Function `mpiexec` provided by `MPI.jl` is a convenient way of accessing the `mpiexec` program that matches the MPI installation used my Julia.\n", + "Note: Function `mpiexec` provided by `MPI.jl` is a convenient way of accessing the `mpiexec` program that matches the MPI installation used by Julia.\n", "
" ] }, @@ -974,7 +974,7 @@ "\n", "\n", "\n", - "`MPI_Send` is also often called a blocking send, but this is very misleading. `MPI_Send` might or not wait for a matching `MPI_Recv`. Look into the following example (which in fact is an incorrect MPI program)." + "`MPI_Send` is also often called a blocking send, but this is very misleading. `MPI_Send` might or not wait for a matching `MPI_Recv`. Assuming that `MPI_Send` will block waiting for a matching receive is erroneous. I.e., we cannot assume that `MPI_Send` has synchronization side effects with the receiver process. However, assuming that `MPI_Send` will not block is also erroneous. Look into the following example (which in fact is an incorrect MPI program). In contrast, `MPI_Send` guarantees that the send buffer can be reused when function returns (complete operation)." ] }, { @@ -1047,7 +1047,7 @@ "source": [ "## Fixing cyclic dependencies\n", "\n", - "Returning to the incorrect program seen before. Remember that on my laptop it worked for `n=1`, but it failed for `n=10000`. For `n=1` the MPI implementation provably decided that the best trade-off was to buffer the small message and return from the `MPI_Send` as soon as possible. For the large message (`n=10000`) it provably decided to wait for a matching receive in ordeer to avoid copying data. In this case, both ranks were blocked on the call to `MPI_Send` and the receives were never posted leading to a dead lock.\n", + "Returning to the incorrect program seen before. Remember that on my laptop it worked for `n=1`, but it failed for `n=10000`. For `n=1` the MPI implementation provably decided that the best trade-off was to buffer the small message and return from the `MPI_Send` as soon as possible. For the large message (`n=10000`) it provably decided to wait for a matching receive in order to avoid copying data. In this case, both ranks were blocked on the call to `MPI_Send` and the receives were never posted leading to a dead lock.\n", "\n", "We can fix these cyclic dependences by smartly ordering the sends and the receives:" ] @@ -1149,10 +1149,11 @@ "source": [ "### Standard mode\n", "\n", - "* Function `MPI_Send` \n", + "* Function `MPI_Send`.\n", "* Programmer cannot make any assumptions whether the message is buffered or not. This is up to the system.\n", - "* Assuming buffering might lead to incorrect programs (as the example above).\n", - "* Assuming that a matching receive started is also incorrect." + "* Assuming that `MPI_Send` does not block because it copies the message into a buffer is erroneous (as the example above).\n", + "* Assuming that `MPI_Send` will block waiting for a matching receive is also incorrect. \n", + "* Assuming that a matching receive started when `MPI_Send`returns is also incorrect (no synchronization guarantee)." ] }, { @@ -1160,9 +1161,9 @@ "id": "014ef71f", "metadata": {}, "source": [ - "### Buffered\n", + "### Buffered mode\n", "\n", - "* Function `MPI_Bsend`\n", + "* Function `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", @@ -1174,12 +1175,12 @@ "id": "bf456a3e", "metadata": {}, "source": [ - "### Synchronous\n", + "### Synchronous mode\n", "\n", - "* Function `MPI_Ssend`\n", - "* It waits for a matching receive and it is guaranteed that the receive started.\n", - "* No extra data copy\n", - "* Easy to get deadlocks\n", + "* Function `MPI_Ssend`.\n", + "* It waits for a matching receive and it is guaranteed that the receive started (synchronization guarantee).\n", + "* No extra data copy.\n", + "* Easy to get deadlocks.\n", "* It can be started whether or not a matching receive was posted." ] }, @@ -1188,9 +1189,9 @@ "id": "a1bd72c3", "metadata": {}, "source": [ - "### Ready\n", + "### Ready mode\n", "\n", - "* Function `MPI_Rsend`\n", + "* Function `MPI_Rsend`.\n", "* It may be started only if the matching receive is already posted. This allows the underlying implementation to skip some operations, such as a [handshake](https://en.wikipedia.org/wiki/Handshake_(computing)).\n", "* Erroneous if there is no matching receive yet.\n", "* Otherwise, the same as the synchronous `MPI_Ssend`." @@ -1242,7 +1243,7 @@ "source": [ "## Incomplete operations\n", "\n", - "Functions `MPI_Isend` and `MPI_Irecv` are *incomplete* operations, hence the `I` in `MPI_Isend` and `MPI_Irecv`. This means that, when these functions return, there is no guarantee that the underlying operation has finished. Function `MPI_Wait` should be used to wait for completion of the send and/or receive.\n", + "Functions `MPI_Isend` and `MPI_Irecv` are *incomplete* operations, hence the `I` in `MPI_Isend` and `MPI_Irecv`. This means that, when these functions return, there is no guarantee that the underlying operation has finished. Function `MPI_Wait` should be used to wait for completion of the send and/or receive.\n", "\n", "In particular:\n", "\n", @@ -1354,7 +1355,7 @@ "\n", "Remember that we used `MPI_Probe` to query for the size of an incoming message. However, if we use `MPI_Probe` we miss the opportunity to do local work before a matching send started, i.e. we cannot do latency hiding.\n", "\n", - "This can be fixed using an `MPI_Iprobe`, i.e., an incomplete probe. It allows us to check for incoming messages without blocking.\n", + "This can be fixed using an `MPI_Iprobe`, i.e., an incomplete probe (aka a non-blocking probe). It allows us to check for incoming messages without blocking.\n", "\n", "In Julia:\n", "```julia\n", @@ -1363,8 +1364,11 @@ "\n", "In C:\n", "```C\n", - "int MPI_Iprobe(int source, int tag, MPI_Comm comm, int *flag,\n", - " MPI_Status *status)\n" + "int MPI_Iprobe(int source, int tag, MPI_Comm comm, int *ismsg,\n", + " MPI_Status *status)\n", + "```\n", + "\n", + "Function `MPI_Iprobe` returns immediately without waiting for a matching send. The value of `ismsg` tells if a send was posted or not. If there was a send, the `status` object can be used to get information about the incoming message as we saw before." ] }, { @@ -1464,7 +1468,7 @@ "source": [ "## Summary\n", "\n", - "We have seen different ways of sending and receiving messages with MPI, each one with its pros and cons. You as user should decide which is the best option for a given problem. We also learned how to get information about an incoming messages with `MPI_Status`, `MPI_Probe`, and `MPI_Iprobe`. In addition, we saw how easy is to write incorrect programs if you do not follow the semantics of the MPI operations properly.\n" + "We have seen different ways of sending and receiving messages with MPI, each one with its pros and cons. You as user should decide which is the best option for a given problem. We also learned how to get information about an incoming messages with `MPI_Status`, `MPI_Probe`, and `MPI_Iprobe`. In addition, we saw how easy is to write incorrect programs with if you do not follow the semantics of the MPI operations properly.\n" ] }, { diff --git a/dev/julia_mpi/index.html b/dev/julia_mpi/index.html index 7744fd1..c6f217a 100644 --- a/dev/julia_mpi/index.html +++ b/dev/julia_mpi/index.html @@ -14,4 +14,4 @@ var myIframe = document.getElementById("notebook"); iFrameResize({log:true}, myIframe); }); - + diff --git a/dev/julia_mpi_src/index.html b/dev/julia_mpi_src/index.html index 1f08b98..1076b31 100644 --- a/dev/julia_mpi_src/index.html +++ b/dev/julia_mpi_src/index.html @@ -8111,7 +8111,7 @@ a.anchor-link {
-Note: Function `mpiexec` provided by `MPI.jl` is a convenient way of accessing the `mpiexec` program that matches the MPI installation used my Julia. +Note: Function `mpiexec` provided by `MPI.jl` is a convenient way of accessing the `mpiexec` program that matches the MPI installation used by Julia.
@@ -8607,7 +8607,7 @@ a.anchor-link {

Blocking operations

MPI_Recv and MPI_Probe are blocking operations. This means that they will wait for a matching send.

-

MPI_Send is also often called a blocking send, but this is very misleading. MPI_Send might or not wait for a matching MPI_Recv. Look into the following example (which in fact is an incorrect MPI program).

+

MPI_Send is also often called a blocking send, but this is very misleading. MPI_Send might or not wait for a matching MPI_Recv. Assuming that MPI_Send will block waiting for a matching receive is erroneous. I.e., we cannot assume that MPI_Send has synchronization side effects with the receiver process. However, assuming that MPI_Send will not block is also erroneous. Look into the following example (which in fact is an incorrect MPI program). In contrast, MPI_Send guarantees that the send buffer can be reused when function returns (complete operation).

@@ -8690,7 +8690,7 @@ a.anchor-link {
-

Fixing cyclic dependencies

Returning to the incorrect program seen before. Remember that on my laptop it worked for n=1, but it failed for n=10000. For n=1 the MPI implementation provably decided that the best trade-off was to buffer the small message and return from the MPI_Send as soon as possible. For the large message (n=10000) it provably decided to wait for a matching receive in ordeer to avoid copying data. In this case, both ranks were blocked on the call to MPI_Send and the receives were never posted leading to a dead lock.

+

Fixing cyclic dependencies

Returning to the incorrect program seen before. Remember that on my laptop it worked for n=1, but it failed for n=10000. For n=1 the MPI implementation provably decided that the best trade-off was to buffer the small message and return from the MPI_Send as soon as possible. For the large message (n=10000) it provably decided to wait for a matching receive in order to avoid copying data. In this case, both ranks were blocked on the call to MPI_Send and the receives were never posted leading to a dead lock.

We can fix these cyclic dependences by smartly ordering the sends and the receives:

@@ -8803,10 +8803,11 @@ a.anchor-link {

Standard mode

@@ -8818,8 +8819,8 @@ a.anchor-link {
-

Buffered

@@ -8852,8 +8853,8 @@ a.anchor-link {
diff --git a/dev/julia_tutorial/index.html b/dev/julia_tutorial/index.html index 33f4a5b..bacfd3c 100644 --- a/dev/julia_tutorial/index.html +++ b/dev/julia_tutorial/index.html @@ -14,4 +14,4 @@ var myIframe = document.getElementById("notebook"); iFrameResize({log:true}, myIframe); }); -
+ diff --git a/dev/matrix_matrix/index.html b/dev/matrix_matrix/index.html index e3f8e76..0b831bc 100644 --- a/dev/matrix_matrix/index.html +++ b/dev/matrix_matrix/index.html @@ -14,4 +14,4 @@ var myIframe = document.getElementById("notebook"); iFrameResize({log:true}, myIframe); }); - + diff --git a/dev/mpi_tutorial/index.html b/dev/mpi_tutorial/index.html index 1a219cc..2d326ba 100644 --- a/dev/mpi_tutorial/index.html +++ b/dev/mpi_tutorial/index.html @@ -14,4 +14,4 @@ var myIframe = document.getElementById("notebook"); iFrameResize({log:true}, myIframe); }); - + diff --git a/dev/notebook-hello/index.html b/dev/notebook-hello/index.html index ecfee72..9de1d19 100644 --- a/dev/notebook-hello/index.html +++ b/dev/notebook-hello/index.html @@ -14,4 +14,4 @@ var myIframe = document.getElementById("notebook"); iFrameResize({log:true}, myIframe); }); - + diff --git a/dev/pdes/index.html b/dev/pdes/index.html index ef8aac6..bdc884f 100644 --- a/dev/pdes/index.html +++ b/dev/pdes/index.html @@ -14,4 +14,4 @@ var myIframe = document.getElementById("notebook"); iFrameResize({log:true}, myIframe); }); - + diff --git a/dev/solutions/index.html b/dev/solutions/index.html index 6b5085e..b753e98 100644 --- a/dev/solutions/index.html +++ b/dev/solutions/index.html @@ -14,4 +14,4 @@ var myIframe = document.getElementById("notebook"); iFrameResize({log:true}, myIframe); }); - + diff --git a/dev/solutions_for_all_notebooks/index.html b/dev/solutions_for_all_notebooks/index.html index 7ef1a3f..6d1b1cd 100644 --- a/dev/solutions_for_all_notebooks/index.html +++ b/dev/solutions_for_all_notebooks/index.html @@ -172,4 +172,4 @@ end

« Jacobi method
+end diff --git a/dev/tsp/index.html b/dev/tsp/index.html index da768b7..2ffb41b 100644 --- a/dev/tsp/index.html +++ b/dev/tsp/index.html @@ -14,4 +14,4 @@ var myIframe = document.getElementById("notebook"); iFrameResize({log:true}, myIframe); }); - +