Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
adapt smpi tuto to use pajengr (+ dockerfile)
[simgrid.git] / docs / source / Tutorial_MPI_Applications.rst
index 35c09a4..f33253c 100644 (file)
@@ -3,11 +3,6 @@
 Simulating MPI Applications
 ===========================
 
-.. warning:: This document is still in early stage. You can try to
-   take this tutorial, but should not be surprised if things fall short.
-   It will be completed for the next release, v3.22, released by the end
-   of 2018.
-
 Discover SMPI
 -------------
 
@@ -67,7 +62,7 @@ occuring between two MPI calls are benchmarked, and the corresponding
 time is reported into the simulator.
 
 .. image:: /tuto_smpi/img/big-picture.svg
-   :align: center         
+   :align: center
 
 Describing Your Platform
 ------------------------
@@ -145,7 +140,7 @@ bandwidth). This link is used for every communication within the
 cluster. The route from ``node-0.simgrid.org`` to ``node-1.simgrid.org``
 counts 3 links: the private link of ``node-0.simgrid.org``, the backbone
 and the private link of ``node-1.simgrid.org``.
-          
+
 .. todo::
 
    Add the picture.
@@ -214,7 +209,7 @@ Dragonfly Cluster
 This topology was introduced to further reduce the amount of links
 while maintaining a high bandwidth for local communications. To model
 this in SimGrid, pass a ``topology="DRAGONFLY"`` attribute to your
-cluster. It's based on the implementation of the topology used on 
+cluster. It's based on the implementation of the topology used on
 Cray XC systems, described in paper
 `Cray Cascade: A scalable HPC system based on a Dragonfly network <https://dl.acm.org/citation.cfm?id=2389136>`_.
 
@@ -222,13 +217,13 @@ System description follows the format ``topo_parameters=#groups;#chassis;#router
 For example, ``3,4 ; 3,2 ; 3,1 ; 2``:
 
 - ``3,4``: There are 3 groups with 4 links between each (blue level).
-  Links to nth group are attached to the nth router of the group 
+  Links to nth group are attached to the nth router of the group
   on our implementation.
 - ``3,2``: In each group, there are 3 chassis with 2 links between each nth router
   of each group (black level)
 - ``3,1``: In each chassis, 3 routers are connected together with a single link
   (green level)
-- ``2``: Each router has two nodes attached (single link) 
+- ``2``: Each router has two nodes attached (single link)
 
 .. image:: ../../examples/platforms/cluster_dragonfly.svg
    :align: center
@@ -279,9 +274,9 @@ container to enjoy the provided dependencies.
    when you log out of the container, so don't edit the other files!
 
 All needed dependencies are already installed in this container
-(SimGrid, the C/C++/Fortran compilers, make, pajeng and R). Vite being
+(SimGrid, the C/C++/Fortran compilers, make, pajeng, R and pajengr). Vite being
 only optional in this tutorial, it is not installed to reduce the
-image size. 
+image size.
 
 The container also include the example platform files from the
 previous section as well as the source code of the NAS Parallel
@@ -308,6 +303,14 @@ Debian and Ubuntu for example, you can get them as follows:
 
    sudo apt install simgrid pajeng make gcc g++ gfortran vite
 
+For R analysis of the produced traces, you may want to install R, 
+and the `pajengr<https://github.com/schnorr/pajengr#installation/>_ package.
+
+.. code-block:: shell
+
+   sudo apt install r-base r-cran-devtools cmake flex bison
+   Rscript -e "library(devtools); install_github('schnorr/pajengr');"
+
 To take this tutorial, you will also need the platform files from the
 previous section as well as the source code of the NAS Parallel
 Benchmarks. Just  clone `this repository
@@ -336,13 +339,13 @@ that comes with the template.
 
 Compiling and Executing
 .......................
-             
+
 Compiling the program is straightforward (double check your
 :ref:`SimGrid installation <install>` if you get an error message):
 
 
 .. code-block:: shell
-               
+
   $ smpicc -O3 roundtrip.c -o roundtrip
 
 
@@ -354,20 +357,20 @@ nodes from the ``cluster_crossbar.xml`` platform as follows:
    $ smpirun -np 16 -platform cluster_crossbar.xml -hostfile cluster_hostfile ./roundtrip
 
 - The ``-np 16`` option, just like in regular MPI, specifies the
-  number of MPI processes to use. 
+  number of MPI processes to use.
 - The ``-hostfile cluster_hostfile`` option, just like in regular
   MPI, specifies the host file. If you omit this option, ``smpirun``
   will deploy the application on the first machines of your platform.
 - The ``-platform cluster_crossbar.xml`` option, **which doesn't exist
   in regular MPI**, specifies the platform configuration to be
-  simulated. 
+  simulated.
 - At the end of the line, one finds the executable name and
   command-line arguments (if any -- roundtrip does not expect any arguments).
 
 Feel free to tweak the content of the XML platform file and the
 program to see the effect on the simulated execution time. It may be
 easier to compare the executions with the extra option
-``--cfg=smpi/display_timing:yes``.  Note that the simulation accounts
+``--cfg=smpi/display-timing:yes``.  Note that the simulation accounts
 for realistic network protocol effects and MPI implementation
 effects. As a result, you may see "unexpected behavior" like in the
 real world (e.g., sending a message 1 byte larger may lead to
@@ -402,7 +405,6 @@ use:
 .. code-block:: shell
 
    smpirun -np 4 -platform ../cluster_backbone.xml -trace --cfg=tracing/filename:lu.S.4.trace bin/lu.S.4
-   pj_dump --ignore-incomplete-links lu.S.4.trace | grep State > lu.S.4.state.csv
 
 You can then produce a Gantt Chart with the following R chunk. You can
 either copy/paste it in a R session, or `turn it into a Rscript executable
@@ -411,10 +413,11 @@ run it again and again.
 
 .. code-block:: R
 
+   library(pajengr)
    library(ggplot2)
 
    # Read the data
-   df_state = read.csv("lu.S.4.state.csv", header=F, strip.white=T)
+   df_state = pajeng_read("lu.S.4.trace")
    names(df_state) = c("Type", "Rank", "Container", "Start", "End", "Duration", "Level", "State");
    df_state = df_state[!(names(df_state) %in% c("Type","Container","Level"))]
    df_state$Rank = as.numeric(gsub("rank-","",df_state$Rank))
@@ -428,7 +431,7 @@ run it again and again.
 
 This produces a file called ``Rplots.pdf`` with the following
 content. You can find more visualization examples `online
-<http://simgrid.gforge.inria.fr/contrib/R_visualization.html>`_.
+<https://simgrid.org/contrib/R_visualization.html>`_.
 
 .. image:: /tuto_smpi/img/lu.S.4.png
    :align: center
@@ -451,25 +454,19 @@ replay it on a different platform with the same amount of nodes. The
 replay is much faster than live simulation, as the computations are
 skipped (the application must be network-dependent for this to work).
 
-You can even generate the trace during as live simulation, as follows:
+You can even generate the trace during the live simulation as follows:
 
 .. code-block:: shell
 
-   $ smpirun -trace-ti --cfg=tracing/filename:LU.A.32 -np 32 -platform ../cluster_backbone.xml bin/lu.A.32 
+   $ smpirun -trace-ti --cfg=tracing/filename:LU.A.32 -np 32 -platform ../cluster_backbone.xml bin/lu.A.32
 
 The produced trace is composed of a file ``LU.A.32`` and a folder
-``LU.A.32_files``. To replay this with SMPI, you need to first compile
-the provided ``smpi_replay.cpp`` file, that comes from
-`simgrid/examples/smpi/replay
-<https://framagit.org/simgrid/simgrid/tree/master/examples/smpi/replay>`_.
+``LU.A.32_files``. You can replay this trace with SMPI thanks to ``smpirun``.
+For example, the following command replays the trace on a different platform:
 
 .. code-block:: shell
 
-   $ smpicxx ../replay.cpp -O3 -o ../smpi_replay
-
-Afterward, you can replay your trace in SMPI as follows:
-
-   $ smpirun -np 32 -platform ../cluster_torus.xml -ext smpi_replay ../smpi_replay LU.A.32
+   $ smpirun -np 32 -platform ../cluster_crossbar.xml -hostfile ../cluster_hostfile -replay LU.A.32
 
 All the outputs are gone, as the application is not really simulated
 here. Its trace is simply replayed. But if you visualize the live
@@ -478,9 +475,12 @@ unchanged. The simulation does not run much faster on this very
 example, but this becomes very interesting when your application
 is computationally hungry.
 
-.. todo:: smpi_replay should be installed by SimGrid, and smpirun interface could be simplified here.
+.. todo::
 
-Lab 3: Execution Sampling on EP
+    The commands should be separated and executed by some CI to make sure
+    the documentation is up-to-date.
+
+Lab 3: Execution Sampling on Matrix Multiplication example
 -------------------------------
 
 The second method to speed up simulations is to sample the computation
@@ -490,9 +490,128 @@ intensive and take time, while being regular enough not to ruin
 simulation accuracy. Furthermore there should not be any MPI calls
 inside such parts of the code.
 
-Use the EP benchmark, class B, 16 processes.
+Use for this part the `gemm_mpi.c
+<https://gitlab.com/PRACE-4IP/CodeVault/raw/master/hpc_kernel_samples/dense_linear_algebra/gemm/mpi/src/gemm_mpi.cpp>`_
+example, which is provided by the `PRACE Codevault repository
+<http://www.prace-ri.eu/prace-codevault/>`_.
+
+The computing part of this example is the matrix multiplication routine
+
+.. literalinclude:: /tuto_smpi/gemm_mpi.cpp
+   :language: c
+   :lines: 4-19
+   
+
+.. code-block:: shell
+
+  $ smpicc -O3 gemm_mpi.cpp -o gemm
+  $ time smpirun -np 16 -platform cluster_crossbar.xml -hostfile cluster_hostfile --cfg=smpi/display-timing:yes --cfg=smpi/running-power:1000000000 ./gemm
+  
+This should end quite quickly, as the size of each matrix is only 1000x1000. 
+But what happens if we want to simulate larger runs ?
+Replace the size by 2000, 3000, and try again.
+
+The simulation time increases a lot, while there are no more MPI calls performed, only computation.
+
+The ``--cfg=smpi/display-timing`` option gives more details about execution, 
+and advises to use sampling if the time spent in computing loops seems too high.
+
+The ``--cfg=smpi/running-power:1000000000`` option sets the speed of the processor used for 
+running the simulation. Here we say that its speed is the same as one of the 
+processors we are simulation (1Gf), so that 1 second of computation is injected 
+as 1 second in the simulation.
+
+.. code-block:: shell
+
+  [5.568556] [smpi_kernel/INFO] Simulated time: 5.56856 seconds.
+
+  The simulation took 24.9403 seconds (after parsing and platform setup)
+  24.0764 seconds were actual computation of the application
+  [5.568556] [smpi_kernel/INFO] More than 75% of the time was spent inside the application code.
+  You may want to use sampling functions or trace replay to reduce this.
+
+So in our case (size 3000) the simulation ran for 25 seconds, and simulated time was 5.57s at the end.
+Computation by itself took 24 seconds, and can quickly grow with larger sizes 
+(as computation is really performed, there will be variability between similar runs).
+
+SMPI provides sampling macros in order to accelerate simulation by sampling iterations 
+of large computation loops, and skip computation after a certain amount of iterations, 
+or when the sampling is stable enough.
+
+The two macros only slightly differ :
+
+- ``SMPI_SAMPLE_GLOBAL`` : specified number of samples is produced by all processors
+- ``SMPI_SAMPLE_LOCAL`` : each process executes a specified number of iterations
+
+So if the size of the computed part varies between processes (imbalance), 
+it's safer to use the LOCAL one.
+
+To use one of them, replacing the external for loop of the multiply routine:
+
+.. code-block:: c
+
+  for (int i = istart; i <= iend; ++i)
+
+by:
+
+.. code-block:: c
+
+  SMPI_SAMPLE_GLOBAL(int i = istart, i <= iend, ++i, 10, 0.005)
+
+First three parameters are the ones from the loop, while the two last ones are for sampling.
+They mean that at most 10 iterations will be performed, and that sampling phase can be exited 
+earlier if a certain stability is reached after less samples.
+
+Now run the code again with various sizes and parameters and check the time taken for the 
+simulation, as well as the resulting simulated time.
+
+.. code-block:: shell
+
+  [5.575691] [smpi_kernel/INFO] Simulated time: 5.57569 seconds.
+  The simulation took 1.23698 seconds (after parsing and platform setup)
+  0.0319454 seconds were actual computation of the application
+
+In this case the simulation only took 1.2 seconds, while the simulated time 
+stayed almost identical.
+
+Obviously the results of the computation will be altered as most of it is skipped, 
+so these macros cannot be used when results are critical for the application behavior 
+(convergence estimation for instance will be wrong on some codes).
+
+
+Lab 4: Memory folding on large allocations
+-------------------------------
+
+Another issue that can be encountered when simulation with SMPI is lack of memory.
+Indeed we are executing all MPI processes on a single node, which can lead to crashes.
+We will use the DT benchmark of the NAS suite to illustrate how to avoid such issues.
+
+With 85 processes and class C, the DT simulated benchmark will try to allocate 35GB of memory
+, which may not be available on the node your are using.
+
+To avoid this we can simply replace the largest calls to malloc and free by calls 
+to ``SMPI_SHARED_MALLOC`` and ``SMPI_SHARED_FREE``.
+This means that all processes will share one single instance of this buffer.
+As for sampling, results will be altered, and it shouldn't be used for control structures.
+
+For DT example, there are three different calls to malloc in the file, and one of them is for a needed structure.
+Find it and replace the two other ones by SMPI_SHARED_MALLOC (there is only one free to replace for both of them).
+
+Once done, you can now run
+
+.. code-block:: shell
+
+   $ make dt NPROCS=85 CLASS=C
+   (compilation logs)
+   $ smpirun -np 85 -platform ../cluster_backbone.xml bin/dt.C.x BH
+   (execution logs)
+
+And simulation should finish without swapping/crashing (Ignore the warning about the return value).
 
-.. todo:: write this section, and the following ones.
+If control structures are also problematic, you can use ``SMPI_PARTIAL_SHARED_MALLOC(size, offsets, offsetscount)`` 
+macro, which will shared only specific parts of the structure between processes, 
+and use specific memory for the important parts.
+It can be freed afterwards with SMPI_SHARED_FREE.
 
 Further Readings
 ----------------