AMPI: Add fsglobals (filesystem) and pipglobals (Process-in-Process) privatization...
[charm.git] / doc / ampi / manual.rst
1 ===================
2 Adaptive MPI (AMPI)
3 ===================
4
5 .. contents::
6    :depth: 3
7
8
9 Introduction
10 ============
11
12 This manual describes Adaptive MPI (AMPI), which is an implementation of
13 the MPI standard [1]_ on top of Charm++. AMPI acts as a regular MPI
14 implementation (akin to MPICH, OpenMPI, MVAPICH, etc.) with several
15 built-in extensions that allow MPI developers to take advantage of
16 Charm++’s dynamic runtime system, which provides support for process
17 virtualization, overlap of communication and computation, load
18 balancing, and fault tolerance with zero to minimal changes to existing
19 MPI codes.
20
21 In this manual, we first describe the philosophy behind Adaptive MPI,
22 then give a brief introduction to Charm++ and rationale for AMPI. We
23 then describe AMPI in detail. Finally we summarize the changes required
24 for existing MPI codes to run with AMPI. Appendices contain the details
25 of installing AMPI, and building and running AMPI programs.
26
27 Overview
28 --------
29
30 Developing parallel Computational Science and Engineering (CSE)
31 applications is a complex task. One has to implement the right physics,
32 develop or choose and code appropriate numerical methods, decide and
33 implement the proper input and output data formats, perform
34 visualizations, and be concerned with correctness and efficiency of the
35 programs. It becomes even more complex for multi-physics coupled
36 simulations, many of which are dynamic and adaptively refined so that
37 load imbalance becomes a major challenge. In addition to imbalance
38 caused by dynamic program behavior, hardware factors such as latencies,
39 variability, and failures must be tolerated by applications. Our
40 philosophy is to lessen the burden of application developers by
41 providing advanced programming paradigms and versatile runtime systems
42 that can handle many common programming and performance concerns
43 automatically and let application programmers focus on the actual
44 application content.
45
46 Many of these concerns can be addressed using the processor
47 virtualization and over-decomposition philosophy of Charm++. Thus, the
48 developer only sees virtual processors and lets the runtime system deal
49 with underlying physical processors. This is implemented in AMPI by
50 mapping MPI ranks to Charm++ user-level threads as illustrated in Figure
51 :numref:`fig_virt`. As an immediate and simple benefit, the
52 programmer can use as many virtual processors ("MPI ranks") as the
53 problem can be easily decomposed to. For example, suppose the problem
54 domain has :math:`n*2^n` parts that can be easily distributed but
55 programming for general number of MPI processes is burdensome, then the
56 developer can have :math:`n*2^n` virtual processors on any number of
57 physical ones using AMPI.
58
59 .. _fig_virt:
60 .. figure:: figs/virtualization.png
61    :width: 4.6in
62
63    MPI ranks are implemented as user-level threads in AMPI rather than
64    Operating System processes.
65
66
67
68 AMPI’s execution model consists of multiple user-level threads per
69 Processing Element (PE). The Charm++ scheduler coordinates execution of
70 these user-level threads (also called Virtual Processors or VPs) and
71 controls execution. These VPs can also migrate between PEs for the
72 purpose of load balancing or other reasons. The number of VPs per PE
73 specifies the virtualization ratio (degree of over-decomposition). For
74 example, in Figure :numref:`fig_virt` the virtualization ratio
75 is :math:`3.5` (there are four VPs on PE 0 and three VPs on PE 1).
76 Figure :numref:`fig_prac` shows how the problem domain can be
77 over-decomposed in AMPI’s VPs as opposed to other MPI implementations.
78
79 .. _fig_prac:
80 .. figure:: figs/prac.png
81    :width: 4.6in
82
83    The problem domain is over-decomposed to more VPs than PEs.
84
85
86
87 Another benefit of virtualization is communication and computation
88 overlap, which is automatically realized in AMPI without programming
89 effort. Techniques such as software pipelining require significant
90 programming effort to achieve this goal and improve performance.
91 However, one can use AMPI to have more virtual processors than physical
92 processors to overlap communication and computation. Each time a VP is
93 blocked for communication, the Charm++ scheduler picks the next VP among
94 those that are ready to execute. In this manner, while some of the VPs
95 of a physical processor are waiting for a message to arrive, others can
96 continue their execution. Thus, performance improves without any changes
97 to the application source code.
98
99 Another potential benefit is that of better cache utilization. With
100 over-decomposition, a smaller subdomain is accessed by a VP repeatedly
101 in different function calls before getting blocked by communication and
102 switching to another VP. That smaller subdomain may fit into cache if
103 over-decomposition is enough. This concept is illustrated in Figure
104 :numref:`fig_virt` where each AMPI rank’s subdomain is smaller
105 than the corresponding MPI subdomain and so may fit into cache memory.
106 Thus, there is a potential performance improvement without changing the
107 source code.
108
109 One important concern is that of load imbalance. New generation parallel
110 applications are dynamically varying, meaning that processors’ load is
111 shifting during execution. In a dynamic simulation application such as
112 rocket simulation, burning solid fuel, sub-scaling for a certain part of
113 the mesh, crack propagation, particle flows all contribute to load
114 imbalance. A centralized load balancing strategy built into an
115 application is impractical since each individual module is developed
116 mostly independently by various developers. In addition, embedding a
117 load balancing strategy in the code complicates it greatly, and
118 programming effort increases significantly. The runtime system is
119 uniquely positioned to deal with load imbalance. Figure
120 :numref:`fig_migrate` shows the runtime system migrating a VP
121 after detecting load imbalance. This domain may correspond to a weather
122 forecast model where there is a storm cell in the top-left quadrant,
123 which requires more computation to simulate. AMPI will then migrate VP 1
124 to balance the division of work across processors and improve
125 performance. Note that incorporating this sort of load balancing inside
126 the application code may take a lot of effort and complicate the code.
127
128 .. _fig_migrate:
129 .. figure:: figs/migrate.png
130    :width: 4.6in
131
132    AMPI can migrate VPs across processes for load balancing.
133
134
135
136 There are many different load balancing strategies built into Charm++
137 that can be selected by an AMPI application developer. Among those, some
138 may fit better for a particular application depending on its
139 characteristics. Moreover, one can write a new load balancer, best
140 suited for an application, by the simple API provided inside Charm++
141 infrastructure. Our approach is based on actual measurement of load
142 information at runtime, and on migrating computations from heavily
143 loaded to lightly loaded processors.
144
145 For this approach to be effective, we need the computation to be split
146 into pieces many more in number than available processors. This allows
147 us to flexibly map and re-map these computational pieces to available
148 processors. This approach is usually called "multi-domain
149 decomposition".
150
151 Charm++, which we use as a runtime system layer for the work described
152 here, simplifies our approach. It embeds an elaborate performance
153 tracing mechanism, a suite of plug-in load balancing strategies,
154 infrastructure for defining and migrating computational load, and is
155 interoperable with other programming paradigms.
156
157 Charm++
158 =======
159
160 Charm++ is an object-oriented parallel programming library for C. It
161 differs from traditional message passing programming libraries (such as
162 MPI) in that Charm++ is "message-driven". Message-driven parallel
163 programs do not block the processor waiting for a message to be
164 received. Instead, each message carries with itself a computation that
165 the processor performs on arrival of that message. The underlying
166 runtime system of Charm++ is called Converse, which implements a
167 "scheduler" that chooses which message to schedule next
168 (message-scheduling in Charm++ involves locating the object for which
169 the message is intended, and executing the computation specified in the
170 incoming message on that object). A parallel object in Charm++ is a C
171 object on which a certain computations can be asked to be performed from
172 remote processors.
173
174 Charm++ programs exhibit latency tolerance since the scheduler always
175 picks up the next available message rather than waiting for a particular
176 message to arrive. They also tend to be modular, because of their
177 object-based nature. Most importantly, Charm++ programs can be
178 *dynamically load balanced*, because the messages are directed at
179 objects and not at processors; thus allowing the runtime system to
180 migrate the objects from heavily loaded processors to lightly loaded
181 processors.
182
183 Since many CSE applications are originally written using MPI, one would
184 have to rewrite existing code if they were to be converted to Charm++ to
185 take advantage of dynamic load balancing and other Charm++ features.
186 This is indeed impractical. However, Converse - the runtime system of
187 Charm++ - supports interoperability between different parallel
188 programming paradigms such as parallel objects and threads. Using this
189 feature, we developed AMPI, which is described in more detail in the
190 next section.
191
192 AMPI
193 ====
194
195 AMPI utilizes the dynamic load balancing and other capabilities of
196 Charm++ by associating a "user-level" thread with each Charm++
197 migratable object. User’s code runs inside this thread, so that it can
198 issue blocking receive calls similar to MPI, and still present the
199 underlying scheduler an opportunity to schedule other computations on
200 the same processor. The runtime system keeps track of the computational
201 loads of each thread as well as the communication graph between AMPI
202 threads, and can migrate these threads in order to balance the overall
203 load while simultaneously minimizing communication overhead.
204
205 AMPI Compliance to MPI Standards
206 --------------------------------
207
208 Currently AMPI supports the MPI-2.2 standard, with preliminary support
209 for most MPI-3.1 features and a collection of extensions explained in
210 detail in this manual. One-sided communication calls in MPI-2 and MPI-3
211 are implemented, but they do not yet take advantage of RMA features.
212 Non-blocking collectives have been defined in AMPI since before
213 MPI-3.0’s adoption of them. Also ROMIO [2]_ has been integrated into
214 AMPI to support parallel I/O features.
215
216 AMPI Extensions to MPI Standards
217 --------------------------------
218
219 The following are AMPI extensions to the MPI standard, which will be
220 explained in detail in this manual. All AMPI extensions to the MPI
221 standard are prefixed with ``AMPI_`` rather than ``MPI_``. All
222 extensions are available in C, C++, and Fortran, with the exception of
223 ``AMPI_Command_argument_count`` and ``AMPI_Get_command_argument`` which
224 are only available in Fortran.
225
226 .. code-block:: none
227
228    AMPI_Migrate          AMPI_Register_pup            AMPI_Get_pup_data
229    AMPI_Migrate_to_pe    AMPI_Set_migratable          AMPI_Evacuate
230    AMPI_Load_set_value   AMPI_Load_start_measure      AMPI_Load_stop_measure
231    AMPI_Iget             AMPI_Iget_wait               AMPI_Iget_data
232    AMPI_Iget_free        AMPI_Type_is_contiguous      AMPI_Register_main
233    AMPI_Yield            AMPI_Suspend                 AMPI_Resume
234    AMPI_Alltoall_medium  AMPI_Alltoall_long
235    AMPI_Register_just_migrated         AMPI_Register_about_to_migrate
236    AMPI_Command_argument_count         AMPI_Get_command_argument
237
238 AMPI provides a set of built-in attributes on all communicators and
239 windows to find the number of the worker thread, process, or host that a
240 rank is currently running on, as well as the total number of worker
241 threads, processes, and hosts in the job. We define a worker thread to
242 be a thread on which one of more AMPI ranks are scheduled. We define a
243 process here as an operating system process, which may contain one or
244 more worker threads. The built-in attributes are ``AMPI_MY_WTH``,
245 ``AMPI_MY_PROCESS``, ``AMPI_NUM_WTHS``, and ``AMPI_NUM_PROCESSES``.
246 These attributes are accessible from any rank by calling
247 ``MPI_Comm_get_attr``, such as:
248
249 .. code-block:: fortran
250
251    ! Fortran:
252    integer :: my_wth, flag, ierr
253    call MPI_Comm_get_attr(MPI_COMM_WORLD, AMPI_MY_WTH, my_wth, flag, ierr)
254
255
256 .. code-block:: c++
257
258    // C/C++:
259    int my_wth, flag;
260    MPI_Comm_get_attr(MPI_COMM_WORLD, AMPI_MY_WTH, &my_wth, &flag);
261
262 AMPI also provides extra communicator types that users can pass to
263 ``MPI_Comm_split_type``: ``AMPI_COMM_TYPE_HOST`` for splitting a
264 communicator into disjoint sets of ranks that share the same physical
265 host, ``AMPI_COMM_TYPE_PROCESS`` for splitting a communicator into
266 disjoint sets of ranks that share the same operating system process, and
267 ``AMPI_COMM_TYPE_WTH``, for splitting a communicator into disjoint sets
268 of ranks that share the same worker thread.
269
270 For parsing Fortran command line arguments, AMPI Fortran programs should
271 use our extension APIs, which are similar to Fortran 2003’s standard
272 APIs. For example:
273
274 .. code-block:: fortran
275
276    integer :: i, argc, ierr
277    integer, parameter :: arg_len = 128
278    character(len=arg_len), dimension(:), allocatable :: raw_arguments
279
280    call AMPI_Command_argument_count(argc)
281    allocate(raw_arguments(argc))
282    do i = 1, size(raw_arguments)
283        call AMPI_Get_command_argument(i, raw_arguments(i), arg_len, ierr)
284    end do
285
286 Name for Main Program
287 ---------------------
288
289 To convert an existing program to use AMPI, the main function or program
290 may need to be renamed. The changes should be made as follows:
291
292 Fortran
293 ~~~~~~~
294
295 You must declare the main program as a subroutine called "MPI_MAIN". Do
296 not declare the main subroutine as a *program* because it will never be
297 called by the AMPI runtime.
298
299 .. code-block:: fortran
300
301    program pgm -> subroutine MPI_Main
302        ...                       ...
303    end program -> end subroutine
304
305 C or C++
306 ~~~~~~~~
307
308 The main function can be left as is, if ``mpi.h`` is included before the
309 main function. This header file has a preprocessor macro that renames
310 main, and the renamed version is called by the AMPI runtime by each
311 thread.
312
313 Global Variable Privatization
314 -----------------------------
315
316 For the before-mentioned benefits to be effective, one needs to map
317 multiple user-level threads onto each processor. Traditional MPI
318 programs assume that the entire processor is allocated to themselves,
319 and that only one thread of control exists within the process’s address
320 space. So, they may safely use global and static variables in the
321 program. However, global and static variables are problematic for
322 multi-threaded environments such as AMPI or OpenMP. This is because
323 there is a single instance of those variables so they will be shared
324 among different threads in the single address space, so if programmers
325 are not careful a wrong result may be produced by the program. Figure
326 :numref:`fig_global` shows an example of a multi-threaded
327 application with two threads in a single process. :math:`var` is a
328 global or static variable in this example. Thread 1 assigns a value to
329 it, then it gets blocked for communication and another thread can
330 continue. Thereby, thread 2 is scheduled next and accesses :math:`var`
331 which is wrong. The semantics of this program needs separate instances
332 of :math:`var` for each of the threads. That is where the need arises to
333 make some transformations to the original MPI program in order to run
334 correctly with AMPI. Note, this is the only change necessary to run an
335 MPI program with AMPI, that the program be thread-safe and have no
336 global or static variables whose values differ across different MPI
337 ranks. Also note that global variables that are constant or are only
338 written to once to the same value across all ranks during initialization
339 are already thread-safe.
340
341 .. _fig_global:
342 .. figure:: figs/global.png
343    :width: 4.6in
344
345    Mutable global or static variables are an issue for AMPI
346
347
348
349 The basic transformation needed to port the MPI program to AMPI is
350 privatization of global variables. With the MPI process model, each MPI
351 node can keep a copy of its own "permanent variables" - variables that
352 are accessible from more than one subroutines without passing them as
353 arguments. Module variables, "saved" subroutine local variables, and
354 common blocks in Fortran90 belong to this category. If such a program is
355 executed without privatization on AMPI, all the AMPI threads that reside
356 in the same process will access the same copy of such variables, which
357 is clearly not the desired semantics. To ensure correct execution of the
358 original source program, it is necessary to make such variables
359 "private" to individual threads. We provide three choices with varying
360 degrees of developer effort required and varying degrees of portability:
361 manual encapsulation of global state, a thread-local storage based
362 automated mechanism, and global offset table based automated mechanism.
363
364 Automatic Thread-Local Storage Swapping
365 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
366
367 Thread Local Store (TLS) was originally employed in kernel threads to
368 localize variables to threads and provide thread safety. It can be used
369 by annotating global/static variable declarations in C with
370 *thread_local*, in C with *__thread* or C11 with *thread_local* or
371 *_Thread_local*, and in Fortran with OpenMP’s *threadprivate*
372 attribute. OpenMP is required for using tlsglobals in Fortran code since
373 Fortran has no other method of using TLS. The *__thread* keyword is not
374 an official extension of the C language, though compiler writers are
375 encouraged to implement this feature.
376
377 It handles both global and static variables and has no context-switching
378 overhead. AMPI provides runtime support for privatizing thread-local
379 variables to user-level threads by changing the TLS segment register
380 when context switching between user-level threads. The runtime overhead
381 is that of changing a single pointer per user-level thread context
382 switch. Currently, Charm++ supports it for x86/x86_64 platforms when
383 using GNU compilers.
384
385 .. code-block:: c++
386
387    // C/C++ example:
388    int myrank;
389    double xyz[100];
390
391 .. code-block:: fortran
392
393    ! Fortran example:
394    integer :: myrank
395    real*8, dimension(100) :: xyz
396
397 For the example above, the following changes to the code handle the
398 global variables:
399
400 .. code-block:: c++
401
402    // C++ example:
403    thread_local int myrank;
404    thread_local double xyz[100];
405
406    // C example:
407    __thread int myrank;
408    __thread double xyz[100];
409
410 .. code-block:: fortran
411
412    ! Fortran example:
413    integer :: myrank
414    real*8, dimension(100) :: xyz
415    !$omp threadprivate(myrank)
416    !$omp threadprivate(xyz)
417
418 The runtime system also should know that TLS-Globals is used at both
419 compile and link time:
420
421 .. code-block:: bash
422
423    $ ampicxx -o example example.C -tlsglobals
424
425 Automatic Process-in-Process Runtime Linking Privatization
426 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
427
428 Process-in-Process (PiP) [PiP2018]_ Globals allows fully automatic
429 privatization of global variables on GNU/Linux systems without
430 modification of user code. All languages (C, C++, Fortran, etc.) are
431 supported. This method currently lacks support for checkpointing and
432 migration, which are necessary for load balancing and fault tolerance.
433 Additionally, overdecomposition is limited to approximately 12 virtual
434 ranks per logical node, though this can be resolved by building a
435 patched version of glibc.
436
437 This method works by combining a specific method of building binaries
438 with a GNU extension to the dynamic linker. First, AMPI's toolchain
439 wrapper compiles your user program as a Position Independent Executable
440 (PIE) and links it against a special shim of function pointers instead
441 of the normal AMPI runtime. It then builds a small loader utility that
442 links directly against AMPI. For each rank, this loader calls the
443 glibc-specific function ``dlmopen`` on the PIE binary with a unique
444 namespace index. The loader uses ``dlsym`` to populate the PIE binary's
445 function pointers and then it calls the entry point. This ``dlmopen``
446 and ``dlsym`` process repeats for each rank. As soon as execution jumps
447 into the PIE binary, any global variables referenced within will appear
448 privatized. This is because PIE binaries locate the global data segment
449 immediately after the code segment so that PIE global variables are
450 accessed relative to the instruction pointer, and because ``dlmopen``
451 creates a separate copy of these segments in memory for each unique
452 namespace index.
453
454 Optionally, the first step in using PiP-Globals is to build PiP-glibc to
455 overcome the limitation on rank count per process. Use the instructions
456 at https://github.com/RIKEN-SysSoft/PiP/blob/pip-1/INSTALL to download
457 an installable PiP package or build PiP-glibc from source by following
458 the ``Patched GLIBC`` section. AMPI may be able to automatically detect
459 PiP's location if installed as a package, but otherwise set and export
460 the environment variable ``PIP_GLIBC_INSTALL_DIR`` to the value of
461 ``<GLIBC_INSTALL_DIR>`` as used in the above instructions. For example:
462
463 .. code-block:: bash
464
465    $ export PIP_GLIBC_INSTALL_DIR=~/pip
466
467 To use PiP-Globals in your AMPI program (with or without PiP-glibc),
468 compile and link with the ``-pipglobals`` parameter:
469
470 .. code-block:: bash
471
472    $ ampicxx -o example.o -c example.cpp -pipglobals
473    $ ampicxx -o example example.o -pipglobals
474
475 No further effort is needed. Global variables in ``example.cpp`` will be
476 automatically privatized when the program is run. Any libraries and
477 shared objects compiled as PIE will also be privatized. However, if
478 these objects call MPI functions, it will be necessary to build them
479 with the AMPI toolchain wrappers, ``-pipglobals``, and potentially also
480 the ``-standalone`` parameter in the case of shared objects. It is
481 recommended to do this in any case so that AMPI can ensure everything is
482 built as PIE.
483
484 Potential future support for checkpointing and migration will require
485 modification of the ``ld-linux.so`` runtime loader to intercept mmap
486 allocations of the previously mentioned segments and redirect them
487 through Isomalloc. The present lack of support for these features mean
488 PiP-Globals is best suited for testing AMPI during exploratory phases
489 of development, and for production jobs not requiring load balancing or
490 fault tolerance.
491
492 Automatic Filesystem-Based Runtime Linking Privatization
493 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
494
495 Filesystem Globals (FS-Globals) was discovered during the development of
496 PiP-Globals and the two are highly similar. Like PiP-Globals, it
497 requires no modification of user code and works with any language.
498 It also currently lacks support for checkpointing and migration,
499 preventing use of load balancing and fault tolerance. Unlike PiP-Globals,
500 it is portable beyond GNU/Linux and has no limits to overdecomposition
501 beyond available disk space.
502
503 FS-Globals works in the same way as PiP-Globals except that instead of
504 specifying namespaces using ``dlmopen``, which is a GNU/Linux-specific
505 feature, this method creates copies of the user's PIE binary on the
506 filesystem for each rank and calls the POSIX-standard ``dlopen``.
507
508 To use FS-Globals, compile and link with the ``-fsglobals`` parameter:
509
510 .. code-block:: bash
511
512    $ ampicxx -o example.o -c example.cpp -fsglobals
513    $ ampicxx -o example example.o -fsglobals
514
515 No additional steps are required. Global variables in ``example.cpp``
516 will be automatically privatized when the program is run. Variables in
517 statically linked libraries will also be privatized if compiled as PIE.
518 It is recommended to achieve this by building with the AMPI toolchain
519 wrappers and ``-fsglobals``, and this is necessary if the libraries call
520 MPI functions. Shared objects are currently not supported by FS-Globals
521 due to the extra overhead of iterating through all dependencies and
522 copying each one per rank while avoiding system components, plus the
523 complexity of ensuring each rank's program binary sees the proper set of
524 objects.
525
526 This method's use of the filesystem is a drawback in that it is slow
527 during startup and can be considered wasteful. Additionally, support for
528 load balancing and fault tolerance would require further development in
529 the future, using the same infrastructure as what PiP-Globals would
530 require. For these reasons FS-Globals is best suited for the R&D phase
531 of AMPI program development and for small jobs, and it may be less
532 suitable for large production environments.
533
534 Automatic Global Offset Table Swapping
535 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
536
537 Thanks to the ELF Object Format, we have successfully automated the
538 procedure of switching the set of user global variables when switching
539 thread contexts. Executable and Linkable Format (ELF) is a common
540 standard file format for Object Files in Unix-like operating systems.
541 ELF maintains a Global Offset Table (GOT) for globals so it is possible
542 to switch GOT contents at thread context-switch by the runtime system.
543
544 The only thing that the user needs to do is pass the flag
545 ``-swapglobals`` at both compile and link time (e.g. "ampicc -o prog
546 prog.c -swapglobals"). This method does not require any changes to the
547 source code and works with any language (C, C++, Fortran, etc). However,
548 it does not handle static variables, has a context switching overhead
549 that grows with the number of global variables, and is incompatible with
550 SMP builds of AMPI, where multiple virtual ranks can execute
551 simultaneously on different scheduler threads within an OS process.
552 Currently, this feature only works on x86 and x86_64 platforms that
553 fully support ELF, and it requires ld version 2.23 or older, or else a
554 patched version of ld 2.24+ that we provide here:
555 https://charm.cs.illinois.edu/gerrit/gitweb?p=libbfd-patches.git;a=tree;f=swapglobals
556
557 Manual Change
558 ~~~~~~~~~~~~~
559
560 We have employed a strategy of argument passing to do this privatization
561 transformation. That is, the global variables are bunched together in a
562 single user-defined type, which is allocated by each thread dynamically
563 or on the stack. Then a pointer to this type is passed from subroutine
564 to subroutine as an argument. Since the subroutine arguments are passed
565 on the stack, which is not shared across all threads, each subroutine
566 when executing within a thread operates on a private copy of the global
567 variables.
568
569 This scheme is demonstrated in the following examples. The original
570 Fortran90 code contains a module ``shareddata``. This module is used in
571 the ``MPI_MAIN`` subroutine and a subroutine ``subA``. Note that
572 ``PROGRAM PGM`` was renamed to ``SUBROUTINE MPI_MAIN`` and ``END PROGRAM``
573 was renamed to ``END SUBROUTINE``.
574
575 .. code-block:: fortran
576
577    !FORTRAN EXAMPLE
578    MODULE shareddata
579      INTEGER :: myrank
580      DOUBLE PRECISION :: xyz(100)
581    END MODULE
582
583    SUBROUTINE MPI_MAIN                               ! Previously PROGRAM PGM
584      USE shareddata
585      include 'mpif.h'
586      INTEGER :: i, ierr
587      CALL MPI_Init(ierr)
588      CALL MPI_Comm_rank(MPI_COMM_WORLD, myrank, ierr)
589      DO i = 1, 100
590        xyz(i) =  i + myrank
591      END DO
592      CALL subA
593      CALL MPI_Finalize(ierr)
594    END SUBROUTINE                                    ! Previously END PROGRAM
595
596    SUBROUTINE subA
597      USE shareddata
598      INTEGER :: i
599      DO i = 1, 100
600        xyz(i) = xyz(i) + 1.0
601      END DO
602    END SUBROUTINE
603
604 .. code-block:: c++
605
606    //C Example
607    #include <mpi.h>
608
609    int myrank;
610    double xyz[100];
611
612    void subA();
613    int main(int argc, char** argv){
614      int i;
615      MPI_Init(&argc, &argv);
616      MPI_Comm_rank(MPI_COMM_WORLD, &myrank);
617      for(i=0;i<100;i++)
618        xyz[i] = i + myrank;
619      subA();
620      MPI_Finalize();
621    }
622
623    void subA(){
624      int i;
625      for(i=0;i<100;i++)
626        xyz[i] = xyz[i] + 1.0;
627    }
628
629 AMPI executes the main subroutine inside a user-level thread as a
630 subroutine.
631
632 Now we transform this program using the argument passing strategy. We
633 first group the shared data into a user-defined type.
634
635 .. code-block:: fortran
636
637    !FORTRAN EXAMPLE
638    MODULE shareddata
639      TYPE chunk ! modified
640        INTEGER :: myrank
641        DOUBLE PRECISION :: xyz(100)
642      END TYPE ! modified
643    END MODULE
644
645 .. code-block:: c++
646
647    //C Example
648    struct shareddata{
649      int myrank;
650      double xyz[100];
651    };
652
653 Now we modify the main subroutine to dynamically allocate this data and
654 change the references to them. Subroutine ``subA`` is then modified to
655 take this data as argument.
656
657 .. code-block:: fortran
658
659    !FORTRAN EXAMPLE
660    SUBROUTINE MPI_Main
661      USE shareddata
662      USE AMPI
663      INTEGER :: i, ierr
664      TYPE(chunk), pointer :: c ! modified
665      CALL MPI_Init(ierr)
666      ALLOCATE(c) ! modified
667      CALL MPI_Comm_rank(MPI_COMM_WORLD, c%myrank, ierr)
668      DO i = 1, 100
669        c%xyz(i) =  i + c%myrank ! modified
670      END DO
671      CALL subA(c)
672      CALL MPI_Finalize(ierr)
673    END SUBROUTINE
674
675    SUBROUTINE subA(c)
676      USE shareddata
677      TYPE(chunk) :: c ! modified
678      INTEGER :: i
679      DO i = 1, 100
680        c%xyz(i) = c%xyz(i) + 1.0 ! modified
681      END DO
682    END SUBROUTINE
683
684 .. code-block:: c++
685
686    //C Example
687    void MPI_Main{
688      int i,ierr;
689      struct shareddata *c;
690      ierr = MPI_Init();
691      c = (struct shareddata*)malloc(sizeof(struct shareddata));
692      ierr = MPI_Comm_rank(MPI_COMM_WORLD, c.myrank);
693      for(i=0;i<100;i++)
694        c.xyz[i] = i + c.myrank;
695      subA(c);
696      ierr = MPI_Finalize();
697    }
698
699    void subA(struct shareddata *c){
700      int i;
701      for(i=0;i<100;i++)
702        c.xyz[i] = c.xyz[i] + 1.0;
703    }
704
705 With these changes, the above program can be made thread-safe. Note that
706 it is not really necessary to dynamically allocate ``chunk``. One could
707 have declared it as a local variable in subroutine ``MPI_Main``. (Or for
708 a small example such as this, one could have just removed the
709 ``shareddata`` module, and instead declared both variables ``xyz`` and
710 ``myrank`` as local variables). This is indeed a good idea if shared
711 data are small in size. For large shared data, it would be better to do
712 heap allocation because in AMPI, the stack sizes are fixed at the
713 beginning (and can be specified from the command line) and stacks do not
714 grow dynamically.
715
716 Source-to-source Transformation
717 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
718
719 Another approach is to do the changes described in the previous scheme
720 automatically. It means that we can use a tool to transform the source
721 code to move global or static variables in an object and pass them
722 around. This approach is portable across systems and compilers and may
723 also improve locality and hence cache utilization. It also does not have
724 the context-switch overhead of swapping globals. We have multiple tools
725 for automating these transformations for different languages. Currently,
726 there is a tool called *Photran*\  [3]_ for refactoring Fortran codes
727 that can do this transformation. It is Eclipse-based and works by
728 constructing Abstract Syntax Trees (ASTs) of the program. We also have a
729 tool built on top of the *ROSE compiler*\  [4]_ that works for C/C++ and
730 Fortran programs that is available upon request. It emits patches for
731 all files containing global variables which can then be applied to the
732 source code.
733
734 Table :numref:`tab:portability` shows portability of
735 different schemes.
736
737 .. _tab:portability:
738 .. table:: Portability of current implementations of three privatization schemes. "Yes" means we have implemented this technique. "Maybe" indicates there are no theoretical problems, but no implementation exists. "No" indicates the technique is impossible on this platform.
739
740    ==================== === ====== ====== ==== ======= ===== =====
741    Privatization Scheme x86 x86_64 Mac OS BG/Q Windows PPC   ARM7
742    ==================== === ====== ====== ==== ======= ===== =====
743    Transformation       Yes Yes    Yes    Yes  Yes     Yes   Yes
744    GOT-Globals          Yes Yes    No     No   No      Yes   Yes
745    TLS-Globals          Yes Yes    Yes    No   Maybe   Maybe Maybe
746    PiP-Globals          Yes Yes    No     No   No      Yes   Yes
747    FS-Globals           Yes Yes    Yes    No   Maybe   Yes   Yes
748    ==================== === ====== ====== ==== ======= ===== =====
749
750 Extensions for Migrations
751 -------------------------
752
753 AMPI provides fully automated support for migrating MPI ranks between
754 nodes of a system without any application-specific code at all. We do so
755 using a memory allocator, Isomalloc, that allocates memory per
756 user-level thread to globally unique virtual memory addresses. This
757 means that every worker thread in the system reserves slices of virtual
758 memory for all user-level threads, allowing transparent migration of
759 stacks and pointers into memory (Isomalloc requires 64-bit virtual
760 memory addresses and support from the operating system for mapping
761 memory to arbitrary virtual addresses). Applications only need to link
762 with Isomalloc to enable automatic migratability, using *-memory
763 isomalloc*.
764
765 For systems that do not support Isomalloc and for users that wish to
766 have more fine-grain control over which application data structures will
767 be copied at migration time, we have added a few calls to AMPI. These
768 include the ability to register thread-specific data with the run-time
769 system, to pack and unpack all of the thread’s data, and to express
770 willingness to migrate.
771
772 Registering User Data
773 ~~~~~~~~~~~~~~~~~~~~~
774
775 When the AMPI runtime system decides that load imbalance exists within
776 the application, it will invoke one of its internal load balancing
777 strategies, which determines the new mapping of AMPI ranks so as to
778 balance the load. Then the AMPI runtime packs up the rank’s state and
779 moves it to its new home processor. AMPI packs up any internal data in
780 use by the rank, including the thread’s stack in use. This means that
781 the local variables declared in subroutines in a rank, which are created
782 on stack, are automatically packed up by the AMPI runtime system.
783 However, it has no way of knowing what other data are in use by the
784 rank. Thus upon starting execution, a rank needs to notify the system
785 about the data that it is going to use (apart from local variables).
786 Even with the data registration, AMPI cannot determine what size the
787 data is, or whether the registered data contains pointers to other
788 places in memory. For this purpose, a packing subroutine also needs to
789 be provided to the AMPI runtime system along with registered data. (See
790 next section for writing packing subroutines.) The call provided by AMPI
791 for doing this is ``AMPI_Register_pup``. This function takes three
792 arguments: a data item to be transported along with the rank, the pack
793 subroutine, and a pointer to an integer which denotes the registration
794 identifier. In C/C++ programs, it may be necessary to use this integer
795 value after migration completes and control returns to the rank with the
796 function ``AMPI_Get_pup_data``.
797
798 Migration
799 ~~~~~~~~~
800
801 The AMPI runtime system could detect load imbalance by itself and invoke
802 the load balancing strategy. However, since the application code is
803 going to pack/unpack the rank’s data, writing the pack subroutine will
804 be complicated if migrations occur at a stage unknown to the
805 application. For example, if the system decides to migrate a rank while
806 it is in initialization stage (say, reading input files), application
807 code will have to keep track of how much data it has read, what files
808 are open etc. Typically, since initialization occurs only once in the
809 beginning, load imbalance at that stage would not matter much.
810 Therefore, we want the demand to perform load balance check to be
811 initiated by the application.
812
813 AMPI provides a subroutine ``AMPI_Migrate(MPI_Info hints);`` for this
814 purpose. Each rank periodically calls ``AMPI_Migrate``. Typical CSE
815 applications are iterative and perform multiple time-steps. One should
816 call ``AMPI_Migrate`` in each rank at the end of some fixed number of
817 timesteps. The frequency of ``AMPI_Migrate`` should be determined by a
818 tradeoff between conflicting factors such as the load balancing
819 overhead, and performance degradation caused by load imbalance. In some
820 other applications, where application suspects that load imbalance may
821 have occurred, as in the case of adaptive mesh refinement; it would be
822 more effective if it performs a couple of timesteps before telling the
823 system to re-map ranks. This will give the AMPI runtime system some time
824 to collect the new load and communication statistics upon which it bases
825 its migration decisions. Note that ``AMPI_Migrate`` does NOT tell the
826 system to migrate the rank, but merely tells the system to check the
827 load balance after all the ranks call ``AMPI_Migrate``. To migrate the
828 rank or not is decided only by the system’s load balancing strategy.
829
830 Essentially, a call to ``AMPI_Migrate`` signifies to the runtime system
831 that the application has reached a point at which it is safe to
832 serialize the local state. Knowing this, the runtime system can act in
833 several ways.
834
835 The MPI_Info object taken as a parameter by ``AMPI_Migrate`` gives users
836 a way to influence the runtime system’s decision-making and behavior.
837 AMPI provides two built-in MPI_Info objects for this, called
838 ``AMPI_INFO_LB_SYNC`` and ``AMPI_INFO_LB_ASYNC``. Synchronous load
839 balancing assumes that the application is already at a synchronization
840 point. Asynchronous load balancing does not assume this.
841
842 Calling ``AMPI_Migrate`` on a rank with pending send requests (i.e. from
843 MPI_Isend) is currently not supported, therefore users should always
844 wait on any outstanding send requests before calling ``AMPI_Migrate``.
845
846 .. code-block:: c++
847
848    // Main time-stepping loop
849    for (int iter=0; iter < max_iters; iter++) {
850
851      // Time step work ...
852
853      if (iter % lb_freq == 0)
854        AMPI_Migrate(AMPI_INFO_LB_SYNC);
855    }
856
857 Note that migrating ranks around the cores and nodes of a system can
858 change which ranks share physical resources, such as memory. A
859 consequence of this is that communicators created via
860 ``MPI_Comm_split_type`` are invalidated by calls to ``AMPI_Migrate``
861 that result in migration which breaks the semantics of that communicator
862 type. The only valid routine to call on such communicators is
863 ``MPI_Comm_free``.
864
865 We also provide callbacks that user code can register with the runtime
866 system to be invoked just before and right after migration:
867 ``AMPI_Register_about_to_migrate`` and ``AMPI_Register_just_migrated``
868 respectively. Note that the callbacks are only invoked on those ranks
869 that are about to actually migrate or have just actually migrated.
870
871 AMPI provide routines for starting and stopping load measurements, and
872 for users to explicitly set the load value of a rank using the
873 following: ``AMPI_Load_start_measure``, ``AMPI_Load_stop_measure``,
874 ``AMPI_Load_reset_measure``, and ``AMPI_Load_set_value``. And since AMPI
875 builds on top of Charm++, users can experiment with the suite of load
876 balancing strategies included with Charm++, as well as write their own
877 strategies based on user-level information and heuristics.
878
879 Packing/Unpacking Thread Data
880 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
881
882 Once the AMPI runtime system decides which ranks to send to which
883 processors, it calls the specified pack subroutine for that rank, with
884 the rank-specific data that was registered with the system using
885 ``AMPI_Register_pup``. If an AMPI application uses Isomalloc, then the
886 system will define the Pack/Unpack routines for the user. This section
887 explains how a subroutine should be written for performing explicit
888 pack/unpack.
889
890 There are three steps for transporting the rank’s data to another
891 processor. First, the system calls a subroutine to get the size of the
892 buffer required to pack the rank’s data. This is called the "sizing"
893 step. In the next step, which is called immediately afterward on the
894 source processor, the system allocates the required buffer and calls the
895 subroutine to pack the rank’s data into that buffer. This is called the
896 "packing" step. This packed data is then sent as a message to the
897 destination processor, where first a rank is created (along with the
898 thread) and a subroutine is called to unpack the rank’s data from the
899 buffer. This is called the "unpacking" step.
900
901 Though the above description mentions three subroutines called by the
902 AMPI runtime system, it is possible to actually write a single
903 subroutine that will perform all the three tasks. This is achieved using
904 something we call a "pupper". A pupper is an external subroutine that is
905 passed to the rank’s pack-unpack-sizing subroutine, and this subroutine,
906 when called in different phases performs different tasks. An example
907 will make this clear:
908
909 Suppose the user data, chunk, is defined as a derived type in Fortran90:
910
911 .. code-block:: fortran
912
913    !FORTRAN EXAMPLE
914    MODULE chunkmod
915      INTEGER, parameter :: nx=4, ny=4, tchunks=16
916      TYPE, PUBLIC :: chunk
917          REAL(KIND=8) t(22,22)
918          INTEGER xidx, yidx
919          REAL(KIND=8), dimension(400):: bxm, bxp, bym, byp
920      END TYPE chunk
921    END MODULE
922
923 .. code-block:: c++
924
925    //C Example
926    struct chunk{
927      double t;
928      int xidx, yidx;
929      double bxm,bxp,bym,byp;
930    };
931
932 Then the pack-unpack subroutine ``chunkpup`` for this chunk module is
933 written as:
934
935 .. code-block:: fortran
936
937    !FORTRAN EXAMPLE
938    SUBROUTINE chunkpup(p, c)
939      USE pupmod
940      USE chunkmod
941      IMPLICIT NONE
942      INTEGER :: p
943      TYPE(chunk) :: c
944
945      call pup(p, c%t)
946      call pup(p, c%xidx)
947      call pup(p, c%yidx)
948      call pup(p, c%bxm)
949      call pup(p, c%bxp)
950      call pup(p, c%bym)
951      call pup(p, c%byp)
952    end subroutine
953
954 .. code-block:: c++
955
956    //C Example
957    void chunkpup(pup_er p, struct chunk c){
958      pup_double(p,c.t);
959      pup_int(p,c.xidx);
960      pup_int(p,c.yidx);
961      pup_double(p,c.bxm);
962      pup_double(p,c.bxp);
963      pup_double(p,c.bym);
964      pup_double(p,c.byp);
965    }
966
967 There are several things to note in this example. First, the same
968 subroutine ``pup`` (declared in module ``pupmod``) is called to
969 size/pack/unpack any type of data. This is possible because of procedure
970 overloading possible in Fortran90. Second is the integer argument ``p``.
971 It is this argument that specifies whether this invocation of subroutine
972 ``chunkpup`` is sizing, packing or unpacking. Third, the integer
973 parameters declared in the type ``chunk`` need not be packed or unpacked
974 since they are guaranteed to be constants and thus available on any
975 processor.
976
977 A few other functions are provided in module ``pupmod``. These functions
978 provide more control over the packing/unpacking process. Suppose one
979 modifies the ``chunk`` type to include allocatable data or pointers that
980 are allocated dynamically at runtime. In this case, when chunk is
981 packed, these allocated data structures should be deallocated after
982 copying them to buffers, and when chunk is unpacked, these data
983 structures should be allocated before copying them from the buffers. For
984 this purpose, one needs to know whether the invocation of ``chunkpup``
985 is a packing one or unpacking one. For this purpose, the ``pupmod``
986 module provides functions ``fpup_isdeleting``\ (``fpup_isunpacking``).
987 These functions return logical value ``.TRUE.`` if the invocation is for
988 packing (unpacking), and ``.FALSE.`` otherwise. The following example
989 demonstrates this:
990
991 Suppose the type ``dchunk`` is declared as:
992
993 .. code-block:: fortran
994
995    !FORTRAN EXAMPLE
996    MODULE dchunkmod
997      TYPE, PUBLIC :: dchunk
998          INTEGER :: asize
999          REAL(KIND=8), pointer :: xarr(:), yarr(:)
1000      END TYPE dchunk
1001    END MODULE
1002
1003 .. code-block:: c++
1004
1005    //C Example
1006    struct dchunk{
1007      int asize;
1008      double* xarr, *yarr;
1009    };
1010
1011 Then the pack-unpack subroutine is written as:
1012
1013 .. code-block:: fortran
1014
1015    !FORTRAN EXAMPLE
1016    SUBROUTINE dchunkpup(p, c)
1017      USE pupmod
1018      USE dchunkmod
1019      IMPLICIT NONE
1020      INTEGER :: p
1021      TYPE(dchunk) :: c
1022
1023      pup(p, c%asize)
1024
1025      IF (fpup_isunpacking(p)) THEN       !! if invocation is for unpacking
1026        allocate(c%xarr(c%asize))
1027        ALLOCATE(c%yarr(c%asize))
1028      ENDIF
1029
1030      pup(p, c%xarr)
1031      pup(p, c%yarr)
1032
1033      IF (fpup_isdeleting(p)) THEN        !! if invocation is for packing
1034        DEALLOCATE(c%xarr)
1035        DEALLOCATE(c%yarr)
1036      ENDIF
1037
1038
1039    END SUBROUTINE
1040
1041 .. code-block:: c++
1042
1043    //C Example
1044    void dchunkpup(pup_er p, struct dchunk c){
1045      pup_int(p,c.asize);
1046      if(pup_isUnpacking(p)){
1047        c.xarr = (double *)malloc(sizeof(double)*c.asize);
1048        c.yarr = (double *)malloc(sizeof(double)*c.asize);
1049      }
1050      pup_doubles(p,c.xarr,c.asize);
1051      pup_doubles(p,c.yarr,c.asize);
1052      if(pup_isPacking(p)){
1053        free(c.xarr);
1054        free(c.yarr);
1055      }
1056    }
1057
1058 One more function ``fpup_issizing`` is also available in module
1059 ``pupmod`` that returns ``.TRUE.`` when the invocation is a sizing one.
1060 In practice one almost never needs to use it.
1061
1062 Charm++ also provides higher-level PUP routines for C++ STL data
1063 structures and Fortran90 data types. The STL PUP routines will deduce
1064 the size of the structure automatically, so that the size of the data
1065 does not have to be passed in to the PUP routine. This facilitates
1066 writing PUP routines for large pre-existing codebases. To use it, simply
1067 include pup_stl.h in the user code. For modern Fortran with pointers and
1068 allocatable data types, AMPI provides a similarly automated PUP
1069 interface called apup. User code can include pupmod and then call apup()
1070 on any array (pointer or allocatable, multi-dimensional) of built-in
1071 types (character, short, int, long, real, double, complex, double
1072 complex, logical) and the runtime will deduce the size and shape of the
1073 array, including unassociated and NULL pointers. Here is the dchunk
1074 example from earlier, written to use the apup interface:
1075
1076 .. code-block:: fortran
1077
1078    !FORTRAN EXAMPLE
1079    SUBROUTINE dchunkpup(p, c)
1080      USE pupmod
1081      USE dchunkmod
1082      IMPLICIT NONE
1083      INTEGER :: p
1084      TYPE(dchunk) :: c
1085
1086      !! no need for asize
1087      !! no isunpacking allocation necessary
1088
1089      apup(p, c%xarr)
1090      apup(p, c%yarr)
1091
1092      !! no isdeleting deallocation necessary
1093
1094    END SUBROUTINE
1095
1096 Calling ``MPI_`` routines or accessing global variables that have been
1097 privatized by use of tlsglobals or swapglobals from inside a user PUP
1098 routine is currently not allowed in AMPI. Users can store MPI-related
1099 information like communicator rank and size in data structures to be be
1100 packed and unpacked before they are needed inside a PUP routine.
1101
1102 Extensions for Checkpointing
1103 ----------------------------
1104
1105 The pack-unpack subroutines written for migrations make sure that the
1106 current state of the program is correctly packed (serialized) so that it
1107 can be restarted on a different processor. Using the *same* subroutines,
1108 it is also possible to save the state of the program to disk, so that if
1109 the program were to crash abruptly, or if the allocated time for the
1110 program expires before completing execution, the program can be
1111 restarted from the previously checkpointed state. Thus, the pack-unpack
1112 subroutines act as the key facility for checkpointing in addition to
1113 their usual role for migration. Just as in load balancing, no
1114 application specific code is required when using Isomalloc: the AMPI
1115 runtime takes care of all the details involved in migrating data.
1116
1117 To perform a checkpoint in an AMPI program, all you have to do is make a
1118 call to ``int AMPI_Migrate(MPI_Info hints)`` with an ``MPI_Info`` object
1119 that specifies how you would like to checkpoint. Checkpointing can be
1120 thought of as migrating AMPI ranks to storage. Users set the
1121 checkpointing policy on an ``MPI_Info`` object’s ``"ampi_checkpoint"``
1122 key to one of the following values: ``"to_file=directory_name"`` or
1123 ``"false"``. To perform checkpointing in memory a built-in MPI_Info
1124 object called ``AMPI_INFO_CHKPT_IN_MEMORY`` is provided.
1125
1126 Checkpointing to file tells the runtime system to save checkpoints in a
1127 given directory. (Typically, in an iterative program, the iteration
1128 number, converted to a character string, can serve as a checkpoint
1129 directory name.) This directory is created, and the entire state of the
1130 program is checkpointed to this directory. One can restart the program
1131 from the checkpointed state (using the same, more, or fewer physical
1132 processors than were checkpointed with) by specifying
1133 ``"+restart directory_name"`` on the command-line.
1134
1135 Checkpointing in memory allows applications to transparently tolerate
1136 failures online. The checkpointing scheme used here is a double
1137 in-memory checkpoint, in which virtual processors exchange checkpoints
1138 pairwise across nodes in each other’s memory such that if one node
1139 fails, that failed node’s AMPI ranks can be restarted by its buddy once
1140 the failure is detected by the runtime system. As long as no two buddy
1141 nodes fail in the same checkpointing interval, the system can restart
1142 online without intervention from the user (provided the job scheduler
1143 does not revoke its allocation). Any load imbalance resulting from the
1144 restart can then be managed by the runtime system. Use of this scheme is
1145 illustrated in the code snippet below.
1146
1147 .. code-block:: c++
1148
1149    // Main time-stepping loop
1150    for (int iter=0; iter < max_iters; iter++) {
1151
1152      // Time step work ...
1153
1154      if (iter % chkpt_freq == 0)
1155        AMPI_Migrate(AMPI_INFO_CHKPT_IN_MEMORY);
1156    }
1157
1158 A value of ``"false"`` results in no checkpoint being done that step.
1159 Note that ``AMPI_Migrate`` is a collective function, meaning every
1160 virtual processor in the program needs to call this subroutine with the
1161 same MPI_Info object. The checkpointing capabilities of AMPI are powered
1162 by the Charm++ runtime system. For more information about
1163 checkpoint/restart mechanisms please refer to the Charm++
1164 manual: :numref:`sec:checkpoint`.
1165
1166 Extensions for Memory Efficiency
1167 --------------------------------
1168
1169 MPI functions usually require the user to preallocate the data buffers
1170 needed before the functions being called. For unblocking communication
1171 primitives, sometimes the user would like to do lazy memory allocation
1172 until the data actually arrives, which gives the opportunities to write
1173 more memory efficient programs. We provide a set of AMPI functions as an
1174 extension to the standard MPI-2 one-sided calls, where we provide a
1175 split phase ``MPI_Get`` called ``AMPI_Iget``. ``AMPI_Iget`` preserves
1176 the similar semantics as ``MPI_Get`` except that no user buffer is
1177 provided to hold incoming data. ``AMPI_Iget_wait`` will block until the
1178 requested data arrives and runtime system takes care to allocate space,
1179 do appropriate unpacking based on data type, and return.
1180 ``AMPI_Iget_free`` lets the runtime system free the resources being used
1181 for this get request including the data buffer. Finally,
1182 ``AMPI_Iget_data`` is the routine used to access the data.
1183
1184 .. code-block:: c++
1185
1186
1187    int AMPI_Iget(MPI_Aint orgdisp, int orgcnt, MPI_Datatype orgtype, int rank,
1188                  MPI_Aint targdisp, int targcnt, MPI_Datatype targtype, MPI_Win win,
1189                  MPI_Request *request);
1190
1191    int AMPI_Iget_wait(MPI_Request *request, MPI_Status *status, MPI_Win win);
1192
1193    int AMPI_Iget_free(MPI_Request *request, MPI_Status *status, MPI_Win win);
1194
1195    int AMPI_Iget_data(void *data, MPI_Status status);
1196
1197 Extensions for Interoperability
1198 -------------------------------
1199
1200 Interoperability between different modules is essential for coding
1201 coupled simulations. In this extension to AMPI, each MPI application
1202 module runs within its own group of user-level threads distributed over
1203 the physical parallel machine. In order to let AMPI know which ranks are
1204 to be created, and in what order, a top level registration routine needs
1205 to be written. A real-world example will make this clear. We have an MPI
1206 code for fluids and another MPI code for solids, both with their main
1207 programs, then we first transform each individual code to run correctly
1208 under AMPI as standalone codes. Aside from the global and static
1209 variable privatization transformations needed, this also involves making
1210 the main program into a subroutine and naming it ``MPI_Main``.
1211
1212 Thus now, we have two ``MPI_Main``\ s, one for the fluids code and one
1213 for the solids code. We now make these codes co-exist within the same
1214 executable, by first renaming these ``MPI_Main``\ s as ``Fluids_Main``
1215 and ``Solids_Main``\  [5]_ writing a subroutine called ``MPI_Setup``.
1216
1217 .. code-block:: fortran
1218
1219    !FORTRAN EXAMPLE
1220    SUBROUTINE MPI_Setup
1221      USE ampi
1222      CALL AMPI_Register_main(Solids_Main)
1223      CALL AMPI_Register_main(Fluids_Main)
1224    END SUBROUTINE
1225
1226 .. code-block:: c++
1227
1228    //C Example
1229    void MPI_Setup(){
1230      AMPI_Register_main(Solids_Main);
1231      AMPI_Register_main(Fluids_Main);
1232    }
1233
1234 This subroutine is called from the internal initialization routines of
1235 AMPI and tells AMPI how many numbers of distinct modules exist, and
1236 which orchestrator subroutines they execute.
1237
1238 The number of ranks to create for each module is specified on the
1239 command line when an AMPI program is run. Appendix B explains how AMPI
1240 programs are run, and how to specify the number of ranks (``+vp``
1241 option). In the above case, suppose one wants to create 128 ranks of
1242 Solids and 64 ranks of Fluids on 32 physical processors, one would
1243 specify those with multiple ``+vp`` options on the command line as:
1244
1245 .. code-block:: bash
1246
1247    $ ./charmrun gen1.x +p 32 +vp 128 +vp 64
1248
1249 This will ensure that multiple modules representing different complete
1250 applications can co-exist within the same executable. They can also
1251 continue to communicate among their own ranks using the same AMPI
1252 function calls to send and receive with communicator argument as
1253 ``MPI_COMM_WORLD``. But this would be completely useless if these
1254 individual applications cannot communicate with each other, which is
1255 essential for building efficient coupled codes. For this purpose, we
1256 have extended the AMPI functionality to allow multiple
1257 "``COMM_WORLD``\ s"; one for each application. These *world
1258 communicators* form a "communicator universe" an array of communicators
1259 aptly called *MPI_COMM_UNIVERSE*. This array of communicators is indexed
1260 [1 . . . ``MPI_MAX_COMM``]. In the current implementation,
1261 ``MPI_MAX_COMM`` is 8, that is, maximum of 8 applications can co-exist
1262 within the same executable.
1263
1264 The order of these ``COMM_WORLD``\ s within ``MPI_COMM_UNIVERSE`` is
1265 determined by the order in which individual applications are registered
1266 in ``MPI_Setup``.
1267
1268 Thus, in the above example, the communicator for the Solids module would
1269 be ``MPI_COMM_UNIVERSE(1)`` and communicator for Fluids module would be
1270 ``MPI_COMM_UNIVERSE(2)``.
1271
1272 Now any rank within one application can communicate with any rank in the
1273 other application using the familiar send or receive AMPI calls by
1274 specifying the appropriate communicator and the rank number within that
1275 communicator in the call. For example if a Solids rank number 36 wants
1276 to send data to rank number 47 within the Fluids module, it calls:
1277
1278 .. code-block:: fortran
1279
1280    !FORTRAN EXAMPLE
1281    INTEGER , PARAMETER :: Fluids_Comm = 2
1282    CALL MPI_Send(InitialTime, 1, MPI_Double_Precision, tag,
1283                  47, MPI_Comm_Universe(Fluids_Comm), ierr)
1284
1285 .. code-block:: c++
1286
1287    //C Example
1288    int Fluids_Comm = 2;
1289    ierr = MPI_Send(InitialTime, 1, MPI_DOUBLE, tag,
1290                    47, MPI_Comm_Universe(Fluids_Comm));
1291
1292 The Fluids rank has to issue a corresponding receive call to receive
1293 this data:
1294
1295 .. code-block:: fortran
1296
1297    !FORTRAN EXAMPLE
1298    INTEGER , PARAMETER :: Solids_Comm = 1
1299    CALL MPI_Recv(InitialTime, 1, MPI_Double_Precision, tag,
1300                  36, MPI_Comm_Universe(Solids_Comm), stat, ierr)
1301
1302 .. code-block:: c++
1303
1304    //C Example
1305    int Solids_Comm = 1;
1306    ierr = MPI_Recv(InitialTime, 1, MPI_DOUBLE, tag,
1307                    36, MPI_Comm_Universe(Solids_Comm), &stat);
1308
1309 Charm++ Interoperability
1310 ------------------------
1311
1312 There is preliminary support for interoperating AMPI programs with Charm++
1313 programs. This allows users to launch an AMPI program with an arbitrary number
1314 of virtual processes in the same executable as a Charm++ program that contains
1315 arbitrary collections of chares, with both AMPI ranks and chares being co-scheduled
1316 by the runtime system. We also provide an entry method ``void injectMsg(int n, char buf[n])``
1317 for chares to communicate with AMPI ranks. An example program can be found in
1318 ``examples/charm++/AMPI-interop``.
1319
1320 Extensions for Sequential Re-run of a Parallel Node
1321 ---------------------------------------------------
1322
1323 In some scenarios, a sequential re-run of a parallel node is desired.
1324 One example is instruction-level accurate architecture simulations, in
1325 which case the user may wish to repeat the execution of a node in a
1326 parallel run in the sequential simulator. AMPI provides support for such
1327 needs by logging the change in the MPI environment on a certain
1328 processors. To activate the feature, build AMPI module with variable
1329 "AMPIMSGLOG" defined, like the following command in charm directory.
1330 (Linking with zlib "-lz" might be required with this, for generating
1331 compressed log file.)
1332
1333 .. code-block:: bash
1334
1335    $ ./build AMPI netlrts-linux-x86_64 -DAMPIMSGLOG
1336
1337 The feature is used in two phases: writing (logging) the environment and
1338 repeating the run. The first logging phase is invoked by a parallel run
1339 of the AMPI program with some additional command line options.
1340
1341 .. code-block:: bash
1342
1343    $ ./charmrun ./pgm +p4 +vp4 +msgLogWrite +msgLogRank 2 +msgLogFilename "msg2.log"
1344
1345 In the above example, a parallel run with 4 worker threads and 4 AMPI
1346 ranks will be executed, and the changes in the MPI environment of worker
1347 thread 2 (also rank 2, starting from 0) will get logged into diskfile
1348 "msg2.log".
1349
1350 Unlike the first run, the re-run is a sequential program, so it is not
1351 invoked by charmrun (and omitting charmrun options like +p4 and +vp4),
1352 and additional command line options are required as well.
1353
1354 .. code-block:: bash
1355
1356    $ ./pgm +msgLogRead +msgLogRank 2 +msgLogFilename "msg2.log"
1357
1358 User Defined Initial Mapping
1359 ----------------------------
1360
1361 You can define the initial mapping of virtual processors (vp) to
1362 physical processors (p) as a runtime option. You can choose from
1363 predefined initial mappings or define your own mappings. The following
1364 predefined mappings are available:
1365
1366 Round Robin
1367    This mapping scheme maps virtual processor to physical processor in
1368    round-robin fashion, i.e. if there are 8 virtual processors and 2
1369    physical processors then virtual processors indexed 0,2,4,6 will be
1370    mapped to physical processor 0 and virtual processors indexed 1,3,5,7
1371    will be mapped to physical processor 1.
1372
1373    .. code-block:: bash
1374
1375       $ ./charmrun ./hello +p2 +vp8 +mapping RR_MAP
1376
1377 Block Mapping
1378    This mapping scheme maps virtual processors to physical processor in
1379    ranks, i.e. if there are 8 virtual processors and 2 physical
1380    processors then virtual processors indexed 0,1,2,3 will be mapped to
1381    physical processor 0 and virtual processors indexed 4,5,6,7 will be
1382    mapped to physical processor 1.
1383
1384    .. code-block:: bash
1385
1386       $ ./charmrun ./hello +p2 +vp8 +mapping BLOCK_MAP
1387
1388 Proportional Mapping
1389    This scheme takes the processing capability of physical processors
1390    into account for mapping virtual processors to physical processors,
1391    i.e. if there are 2 processors running at different frequencies, then
1392    the number of virtual processors mapped to processors will be in
1393    proportion to their processing power. To make the load balancing
1394    framework aware of the heterogeneity of the system, the flag
1395    *+LBTestPESpeed* should also be used.
1396
1397    .. code-block:: bash
1398
1399       $ ./charmrun ./hello +p2 +vp8 +mapping PROP_MAP
1400       $ ./charmrun ./hello +p2 +vp8 +mapping PROP_MAP +balancer GreedyLB +LBTestPESpeed
1401
1402 If you want to define your own mapping scheme, please contact us for
1403 assistance.
1404
1405 Performance Visualization
1406 -------------------------
1407
1408 AMPI users can take advantage of Charm++’s tracing framework and
1409 associated performance visualization tool, Projections. Projections
1410 provides a number of different views of performance data that help users
1411 diagnose performance issues. Along with the traditional Timeline view,
1412 Projections also offers visualizations of load imbalance and
1413 communication-related data.
1414
1415 In order to generate tracing logs from an application to view in
1416 Projections, link with ``ampicc -tracemode projections``.
1417
1418 AMPI defines the following extensions for tracing support:
1419
1420 .. code-block:: none
1421
1422    AMPI_Trace_begin                      AMPI_Trace_end
1423
1424 When using the *Timeline* view in Projections, AMPI users can visualize
1425 what each VP on each processor is doing (what MPI method it is running
1426 or blocked in) by clicking the *View* tab and then selecting *Show
1427 Nested Bracketed User Events* from the drop down menu. See the
1428 Projections manual for information on performance analysis and
1429 visualization.
1430
1431 AMPI users can also use any tracing libraries or tools that rely on
1432 MPI’s PMPI profiling interface, though such tools may not be aware of
1433 AMPI process virtualization.
1434
1435 Compiling AMPI Programs
1436 -----------------------
1437
1438 AMPI provides a cross-platform compile-and-link script called *ampicc*
1439 to compile C, C++, and Fortran AMPI programs. This script resides in the
1440 ``bin`` subdirectory in the Charm++ installation directory. The main
1441 purpose of this script is to deal with the differences of various
1442 compiler names and command-line options across various machines on which
1443 AMPI runs. It is recommended that the AMPI compiler scripts be used to
1444 compile and link AMPI programs. One major advantage of using these is
1445 that one does not have to specify which libraries are to be linked for
1446 ensuring that C++ and Fortran90 codes are linked together correctly.
1447 Appropriate libraries required for linking such modules together are
1448 known to *ampicc* for various machines.
1449
1450 In spite of the platform-neutral syntax of *ampicc*, one may have to
1451 specify some platform-specific options for compiling and building AMPI
1452 codes. Fortunately, if *ampicc* does not recognize any particular
1453 options on its command line, it promptly passes it to all the individual
1454 compilers and linkers it invokes to compile the program. See the
1455 appendix for more details on building and running AMPI programs.
1456
1457 .. _adaptive-mpi-ampi-codes:
1458
1459 AMPI Example Applications
1460 -------------------------
1461
1462 | This section contains a list of applications that have been written or
1463   adapted to work with AMPI. Most applications are available on git:
1464 | ``git clone ssh://charm.cs.illinois.edu:9418/benchmarks/ampi-benchmarks``.
1465
1466 Most benchmarks can be compiled with the provided top-level Makefile:
1467
1468 .. code-block:: bash
1469
1470        $ git clone ssh://charm.cs.illinois.edu:9418/benchmarks/ampi-benchmarks
1471        $ cd ampi-benchmarks
1472        $ make -f Makefile.ampi
1473
1474 Mantevo project v3.0
1475 ~~~~~~~~~~~~~~~~~~~~
1476
1477 Set of mini-apps from the Mantevo project. Download at
1478 https://mantevo.org/download/.
1479
1480 MiniFE
1481 ^^^^^^
1482
1483 -  Mantevo mini-app for unstructured implicit Finite Element
1484    computations.
1485
1486 -  No changes necessary to source to run on AMPI. Modify file
1487    ``makefile.ampi`` and change variable ``AMPIDIR`` to point to your
1488    Charm++ directory, execute ``make -f makefile.ampi`` to build the
1489    program.
1490
1491 -  Refer to the ``README`` file on how to run the program. For example:
1492    ``./charmrun +p4 ./miniFE.x nx=30 ny=30 nz=30 +vp32``
1493
1494 MiniMD v2.0
1495 ^^^^^^^^^^^
1496
1497 -  Mantevo mini-app for particle interaction in a Lennard-Jones system,
1498    as in the LAMMPS MD code.
1499
1500 -  No changes necessary to source code. Modify file ``Makefile.ampi``
1501    and change variable ``AMPIDIR`` to point to your Charm++ directory,
1502    execute ``make ampi`` to build the program.
1503
1504 -  Refer to the ``README`` file on how to run the program. For example:
1505    ``./charmrun +p4 ./miniMD_ampi +vp32``
1506
1507 CoMD v1.1
1508 ^^^^^^^^^
1509
1510 -  Mantevo mini-app for molecular dynamics codes:
1511    https://github.com/exmatex/CoMD
1512
1513 -  To AMPI-ize it, we had to remove calls to not thread-safe
1514    ``getopt()``. Support for dynamic load balancing has been added in
1515    the main loop and the command line options. It will run on all
1516    platforms.
1517
1518 -  Just update the Makefile to point to AMPI compilers and run with the
1519    provided run scripts.
1520
1521 MiniXYCE v1.0
1522 ^^^^^^^^^^^^^
1523
1524 -  Mantevo mini-app for discrete analog circuit simulation, version 1.0,
1525    with serial, MPI, OpenMP, and MPI+OpenMP versions.
1526
1527 -  No changes besides Makefile necessary to run with virtualization. To
1528    build, do ``cp common/generate_info_header miniXyce_ref/.``, modify
1529    the CC path in ``miniXyce_ref/`` and run ``make``. Run scripts are in
1530    ``test/``.
1531
1532 -  Example run command:
1533    ``./charmrun +p3 ./miniXyce.x +vp3 -circuit ../tests/cir1.net -t_start 1e-6 -pf params.txt``
1534
1535 HPCCG v1.0
1536 ^^^^^^^^^^
1537
1538 -  Mantevo mini-app for sparse iterative solves using the Conjugate
1539    Gradient method for a problem similar to that of MiniFE.
1540
1541 -  No changes necessary except to set compilers in ``Makefile`` to the
1542    AMPI compilers.
1543
1544 -  Run with a command such as:
1545    ``./charmrun +p2 ./test_HPCCG 20 30 10 +vp16``
1546
1547 MiniAMR v1.0
1548 ^^^^^^^^^^^^
1549
1550 -  miniAMR applies a stencil calculation on a unit cube computational
1551    domain, which is refined over time.
1552
1553 -  No changes if using swap-globals. Explicitly extern global variables
1554    if using TLS.
1555
1556 Not yet AMPI-zed (reason):
1557 ^^^^^^^^^^^^^^^^^^^^^^^^^^
1558
1559 MiniAero v1.0 (build issues), MiniGhost v1.0.1 (globals), MiniSMAC2D
1560 v2.0 (globals), TeaLeaf v1.0 (globals), CloverLeaf v1.1 (globals),
1561 CloverLeaf3D v1.0 (globals).
1562
1563 LLNL ASC Proxy Apps
1564 ~~~~~~~~~~~~~~~~~~~
1565
1566 LULESH v2.0
1567 ^^^^^^^^^^^
1568
1569 -  LLNL Unstructured Lagrangian-Eulerian Shock Hydrodynamics proxy app:
1570    https://codesign.llnl.gov/lulesh.php
1571
1572 -  Charm++, MPI, MPI+OpenMP, Liszt, Loci, Chapel versions all exist for
1573    comparison.
1574
1575 -  Manually privatized version of LULESH 2.0, plus a version with PUP
1576    routines in subdirectory ``pup_lulesh202/``.
1577
1578 AMG 2013
1579 ^^^^^^^^
1580
1581 -  LLNL ASC proxy app: Algebraic Multi-Grid solver for linear systems
1582    arising from unstructured meshes:
1583    https://codesign.llnl.gov/amg2013.php
1584
1585 -  AMG is based on HYPRE, both from LLNL. The only change necessary to
1586    get AMG running on AMPI with virtualization is to remove calls to
1587    HYPRE’s timing interface, which is not thread-safe.
1588
1589 -  To build, point the CC variable in Makefile.include to your AMPI CC
1590    wrapper script and ``make``. Executable is ``test/amg2013``.
1591
1592 Lassen v1.0
1593 ^^^^^^^^^^^
1594
1595 -  LLNL ASC mini-app for wave-tracking applications with dynamic load
1596    imbalance. Reference versions are serial, MPI, Charm++, and
1597    MPI/Charm++ interop: https://codesign.llnl.gov/lassen.php
1598
1599 -  No changes necessary to enable AMPI virtualization. Requires some
1600    C++11 support. Set ``AMPIDIR`` in Makefile and ``make``. Run with:
1601    ``./charmrun +p4 ./lassen_mpi +vp8 default 2 2 2 50 50 50``
1602
1603 Kripke v1.1
1604 ^^^^^^^^^^^
1605
1606 -  LLNL ASC proxy app for ARDRA, a full Sn deterministic particle
1607    transport application: https://codesign.llnl.gov/kripke.php
1608
1609 -  Charm++, MPI, MPI+OpenMP, MPI+RAJA, MPI+CUDA, MPI+OCCA versions exist
1610    for comparison.
1611
1612 -  Kripke requires no changes between MPI and AMPI since it has no
1613    global/static variables. It uses cmake so edit the cmake toolchain
1614    files in ``cmake/toolchain/`` to point to the AMPI compilers, and
1615    build in a build directory:
1616
1617    .. code-block:: bash
1618
1619       $ mkdir build; cd build;
1620       $ cmake .. -DCMAKE_TOOLCHAIN_FILE=../cmake/Toolchain/linux-gcc-ampi.cmake
1621       -DENABLE_OPENMP=OFF
1622       $ make
1623
1624    Run with:
1625
1626    .. code-block:: bash
1627
1628       $ ./charmrun +p8 ./src/tools/kripke +vp8 --zones 64,64,64 --procs 2,2,2 --nest ZDG
1629
1630 MCB v1.0.3 (2013)
1631 ^^^^^^^^^^^^^^^^^
1632
1633 -  LLNL ASC proxy app for Monte Carlo particle transport codes:
1634    https://codesign.llnl.gov/mcb.php
1635
1636 -  MPI+OpenMP reference version.
1637
1638 -  Run with:
1639
1640    .. code-block:: bash
1641
1642       $ OMP_NUM_THREADS=1 ./charmrun +p4 ./../src/MCBenchmark.exe --weakScaling
1643        --distributedSource --nCores=1 --numParticles=20000 --multiSigma --nThreadCore=1 +vp16
1644
1645 .. _not-yet-ampi-zed-reason-1:
1646
1647 Not yet AMPI-zed (reason)
1648 ^^^^^^^^^^^^^^^^^^^^^^^^^
1649
1650 : UMT 2013 (global variables).
1651
1652 Other Applications
1653 ~~~~~~~~~~~~~~~~~~
1654
1655 MILC 7.0
1656 ^^^^^^^^
1657
1658 -  MILC is a code to study quantum chromodynamics (QCD) physics.
1659    http://www.nersc.gov/users/computational-systems/cori/nersc-8-procurement/trinity-nersc-8-rfp/nersc-8-trinity-benchmarks/milc/
1660
1661 -  Moved ``MPI_Init_thread`` call to ``main()``, added ``__thread`` to
1662    all global/static variable declarations. Runs on AMPI with
1663    virtualization when using -tlsglobals.
1664
1665 -  Build: edit ``ks_imp_ds/Makefile`` to use AMPI compiler wrappers, run
1666    ``make su3_rmd`` in ``ks_imp_ds/``
1667
1668 -  Run with: ``./su3_rmd +vp8 ../benchmark_n8/single_node/n8_single.in``
1669
1670 SNAP v1.01 (C version)
1671 ^^^^^^^^^^^^^^^^^^^^^^
1672
1673 -  LANL proxy app for PARTISN, an Sn deterministic particle transport
1674    application: https://github.com/losalamos/SNAP
1675
1676 -  SNAP is an update to Sweep3D. It simulates the same thing as Kripke,
1677    but with a different decomposition and slight algorithmic
1678    differences. It uses a 1- or 2-dimensional decomposition and the KBA
1679    algorithm to perform parallel sweeps over the 3-dimensional problem
1680    space. It contains all of the memory, computation, and network
1681    performance characteristics of a real particle transport code.
1682
1683 -  Original SNAP code is Fortran90-MPI-OpenMP, but this is a
1684    C-MPI-OpenMP version of it provided along with the original version.
1685    The Fortran90 version will require global variable privatization,
1686    while the C version works out of the box on all platforms.
1687
1688 -  Edit the Makefile for AMPI compiler paths and run with:
1689    ``./charmrun +p4 ./snap +vp4 --fi center_src/fin01 --fo center_src/fout01``
1690
1691 Sweep3D
1692 ^^^^^^^
1693
1694 -  Sweep3D is a *particle transport* program that analyzes the flux of
1695    particles along a space. It solves a three-dimensional particle
1696    transport problem.
1697
1698 -  This mini-app has been deprecated, and replaced at LANL by SNAP
1699    (above).
1700
1701 -  Build/Run Instructions:
1702
1703    -  Modify the ``makefile`` and change variable CHARMC to point to
1704       your Charm++ compiler command, execute ``make mpi`` to build the
1705       program.
1706
1707    -  Modify file ``input`` to set the different parameters. Refer to
1708       file ``README`` on how to change those parameters. Run with:
1709       ``./charmrun ./sweep3d.mpi +p8 +vp16``
1710
1711 PENNANT v0.8
1712 ^^^^^^^^^^^^
1713
1714 -  Unstructured mesh Rad-Hydro mini-app for a full application at LANL
1715    called FLAG. https://github.com/losalamos/PENNANT
1716
1717 -  Written in C++, only global/static variables that need to be
1718    privatized are mype and numpe. Done manually.
1719
1720 -  Legion, Regent, MPI, MPI+OpenMP, MPI+CUDA versions of PENNANT exist
1721    for comparison.
1722
1723 -  For PENNANT-v0.8, point CC in Makefile to AMPICC and just ’make’. Run
1724    with the provided input files, such as:
1725    ``./charmrun +p2 ./build/pennant +vp8 test/noh/noh.pnt``
1726
1727 Benchmarks
1728 ~~~~~~~~~~
1729
1730 Jacobi-2D (Fortran)
1731 ^^^^^^^^^^^^^^^^^^^
1732
1733 -  Jacobi-2D with 1D decomposition. Problem size and number of
1734    iterations are defined in the source code. Manually privatized.
1735
1736 Jacobi-3D (C)
1737 ^^^^^^^^^^^^^
1738
1739 -  Jacobi-3D with 3D decomposition. Manually privatized. Includes
1740    multiple versions: Isomalloc, PUP, FT, LB, Isend/Irecv, Iput/Iget.
1741
1742 NAS Parallel Benchmarks (NPB 3.3)
1743 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1744
1745 -  A collection of kernels used in different scientific applications.
1746    They are mainly implementations of various linear algebra methods.
1747    http://www.nas.nasa.gov/Resources/Software/npb.html
1748
1749 -  Build/Run Instructions:
1750
1751    -  Modify file ``config/make.def`` to make variable ``CHAMRDIR``
1752       point to the right Charm++ directory.
1753
1754    -  Use ``make <benchmark> NPROCS=<P> CLASS=<C>`` to build a
1755       particular benchmark. The values for ``<benchmark>`` are (bt, cg,
1756       dt, ep, ft, is, lu, mg, sp), ``<P>`` is the number of ranks and
1757       ``<C>`` is the class or the problem size (to be chosen from
1758       A,B,C,D or E). Some benchmarks may have restrictions on values of
1759       ``<P>`` and ``<C>``. For instance, to make CG benchmark with 256
1760       ranks and class C, we will use the following command:
1761       ``make cg NPROCS=256``
1762
1763    -  The resulting executable file will be generated in the respective
1764       directory for the benchmark. In the previous example, a file
1765       *cg.256.C* will appear in the *CG* and ``bin/`` directories. To
1766       run the particular benchmark, you must follow the standard
1767       procedure of running AMPI programs:
1768       ``./charmrun ./cg.C.256 +p64 +vp256 ++nodelist nodelist +isomalloc_sync``
1769
1770 NAS PB Multi-Zone Version (NPB-MZ 3.3)
1771 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1772
1773 -  A multi-zone version of BT, SP and LU NPB benchmarks. The multi-zone
1774    intentionally divides the space unevenly among ranks and causes load
1775    imbalance. The original goal of multi-zone versions was to offer an
1776    test case for hybrid MPI+OpenMP programming, where the load imbalance
1777    can be dealt with by increasing the number of threads in those ranks
1778    with more computation.
1779    http://www.nas.nasa.gov/Resources/Software/npb.html
1780
1781 -  The BT-MZ program shows the heaviest load imbalance.
1782
1783 -  Build/Run Instructions:
1784
1785    -  Modify file ``config/make.def`` to make variable ``CHAMRDIR``
1786       point to the right Charm++ build.
1787
1788    -  Use the format ``make <benchmark> NPROCS=<P> CLASS=<C>`` to build
1789       a particular benchmark. The values for ``<benchmark>`` are (bt-mz,
1790       lu-mz, sp-mz), ``<P>`` is the number of ranks and ``<C>`` is the
1791       class or the problem size (to be chosen from A,B,C,D or E). Some
1792       benchmarks may have restrictions on values of ``<P>`` and ``<C>``.
1793       For instance, to make the BT-MZ benchmark with 256 ranks and class
1794       C, you can use the following command:
1795       ``make bt-mz NPROCS=256 CLASS=C``
1796
1797    -  The resulting executable file will be generated in the *bin/*
1798       directory. In the previous example, a file *bt-mz.256.C* will be
1799       created in the ``bin`` directory. To run the particular benchmark,
1800       you must follow the standard procedure of running AMPI programs:
1801       ``./charmrun ./bt-mz.C.256 +p64 +vp256 ++nodelist nodelist +isomalloc_sync``
1802
1803 HPCG v3.0
1804 ^^^^^^^^^
1805
1806 -  High Performance Conjugate Gradient benchmark, version 3.0. Companion
1807    metric to Linpack, with many vendor-optimized implementations
1808    available: http://hpcg-benchmark.org/
1809
1810 -  No AMPI-ization needed. To build, modify ``setup/Make.AMPI`` for
1811    compiler paths, do
1812    ``mkdir build && cd build && configure ../setup/Make.AMPI && make``.
1813    To run, do ``./charmrun +p16 ./bin/xhpcg +vp64``
1814
1815 Intel Parallel Research Kernels (PRK) v2.16
1816 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1817
1818 -  A variety of kernels (Branch, DGEMM, Nstream, Random, Reduce, Sparse,
1819    Stencil, Synch_global, Synch_p2p, and Transpose) implemented for a
1820    variety of runtimes (SERIAL, OpenMP, MPI-1, MPI-RMA, MPI-SHM,
1821    MPI+OpenMP, SHMEM, FG_MPI, UPC, Grappa, Charm++, and AMPI).
1822    https://github.com/ParRes/Kernels
1823
1824 -  For AMPI tests, set ``CHARMTOP`` and run: ``make allampi``. There are
1825    run scripts included.
1826
1827 OSU Microbenchmarks
1828 ^^^^^^^^^^^^^^^^^^^
1829
1830 MPI collectives performance testing suite.
1831 https://charm.cs.illinois.edu/gerrit/#/admin/projects/benchmarks/osu-collectives-benchmarking
1832
1833 -  Build with: ``./configure CC=~/charm/bin/ampicc && make``
1834
1835 Third Party Open Source Libraries
1836 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1837
1838 HYPRE-2.11.1
1839 ^^^^^^^^^^^^
1840
1841 -  High Performance Preconditioners and solvers library from LLNL.
1842    https://computation.llnl.gov/project/linear_solvers/software.php
1843
1844 -  Hypre-2.11.1 builds on top of AMPI using the configure command:
1845
1846    .. code-block:: bash
1847
1848       $ ./configure --with-MPI \
1849             CC=~/charm/bin/ampicc \
1850             CXX=~/charm/bin/ampicxx \
1851             F77=~/charm/bin/ampif77 \
1852             --with-MPI-include=~/charm/include \
1853             --with-MPI-lib-dirs=~/charm/lib \
1854             --with-MPI-libs=mpi --without-timing --without-print-errors
1855       $ make -j8
1856
1857 -  All HYPRE tests and examples pass tests with virtualization,
1858    migration, etc. except for those that use Hypre’s timing interface,
1859    which uses a global variable internally. So just remove those calls
1860    and do not define ``HYPRE_TIMING`` when compiling a code that uses
1861    Hypre. In the examples directory, you’ll have to set the compilers to
1862    your AMPI compilers explicitly too. In the test directory, you’ll
1863    have to edit the Makefile to 1) Remove ``-DHYPRE_TIMING`` from both
1864    ``CDEFS`` and ``CXXDEFS``, 2) Remove both ``${MPILIBS}`` and
1865    ``${MPIFLAGS}`` from ``MPILIBFLAGS``, and 3) Remove ``${LIBS}`` from
1866    ``LIBFLAGS``. Then run ``make``.
1867
1868 -  To run the ``new_ij`` test, run:
1869    ``./charmrun +p64 ./new_ij -n 128 128 128 -P 4 4 4 -intertype 6 -tol 1e-8 -CF 0 -solver 61 -agg_nl 1 27pt -Pmx 6 -ns 4 -mu 1 -hmis -rlx 13 +vp64``
1870
1871 MFEM-3.2
1872 ^^^^^^^^
1873
1874 -  MFEM is a scalable library for Finite Element Methods developed at
1875    LLNL. http://mfem.org/
1876
1877 -  MFEM-3.2 builds on top of AMPI (and METIS-4.0.3 and HYPRE-2.11.1).
1878    Download MFEM,
1879    `HYPRE <https://computation.llnl.gov/project/linear_solvers/software.php>`__,
1880    and `METIS <http://glaros.dtc.umn.edu/gkhome/fsroot/sw/metis/OLD>`__.
1881    Untar all 3 in the same top-level directory.
1882
1883 -  Build HYPRE-2.11.1 as described above.
1884
1885 -  Build METIS-4.0.3 by doing ``cd metis-4.0.3/ && make``
1886
1887 -  Build MFEM-3.2 serial first by doing ``make serial``
1888
1889 -  Build MFEM-3.2 parallel by doing:
1890
1891    -  First, comment out ``#define HYPRE_TIMING`` in
1892       ``mfem/linalg/hypre.hpp``. Also, you must add a
1893       ``#define hypre_clearTiming()`` at the top of
1894       ``linalg/hypre.cpp``, because Hypre-2.11.1 has a bug where it
1895       doesn’t provide a definition of this function if you don’t define
1896       ``HYPRE_TIMING``.
1897
1898    -  ``make parallel MFEM_USE_MPI=YES MPICXX=~/charm/bin/ampicxx HYPRE_DIR=~/hypre-2.11.1/src/hypre METIS_DIR=~/metis-4.0.3``
1899
1900 -  To run an example, do
1901    ``./charmrun +p4 ./ex15p -m ../data/amr-quad.mesh +vp16``. You may
1902    want to add the runtime options ``-no-vis`` and ``-no-visit`` to
1903    speed things up.
1904
1905 -  All example programs and miniapps pass with virtualization, and
1906    migration if added.
1907
1908 XBraid-1.1
1909 ^^^^^^^^^^
1910
1911 -  XBraid is a scalable library for parallel time integration using
1912    MultiGrid, developed at LLNL.
1913    https://computation.llnl.gov/project/parallel-time-integration/software.php
1914
1915 -  XBraid-1.1 builds on top of AMPI (and its examples/drivers build on
1916    top of MFEM-3.2, HYPRE-2.11.1, and METIS-4.0.3 or METIS-5.1.0).
1917
1918 -  To build XBraid, modify the variables CC, MPICC, and MPICXX in
1919    makefile.inc to point to your AMPI compilers, then do ``make``.
1920
1921 -  To build XBraid’s examples/ and drivers/ modify the paths to MFEM and
1922    HYPRE in their Makefiles and ``make``.
1923
1924 -  To run an example, do
1925    ``./charmrun +p2 ./ex-02 -pgrid 1 1 8 -ml 15 -nt 128 -nx 33 33 -mi 100 +vp8 ++local``.
1926
1927 -  To run a driver, do
1928    ``./charmrun +p4 ./drive-03 -pgrid 2 2 2 2 -nl 32 32 32 -nt 16 -ml 15 +vp16 ++local``
1929
1930 Other AMPI codes
1931 ~~~~~~~~~~~~~~~~
1932
1933 -  FLASH
1934
1935 -  BRAMS (Weather prediction model)
1936
1937 -  CGPOP
1938
1939 -  Fractography3D (Crack Propagation)
1940
1941 -  JetAlloc
1942
1943 -  PlasComCM (XPACC)
1944
1945 -  PlasCom2 (XPACC)
1946
1947 -  Harm3D
1948
1949 Installing AMPI
1950 ===============
1951
1952 AMPI is included in the source distribution of Charm++. To get the
1953 latest sources from PPL, visit: http://charm.cs.illinois.edu/software
1954
1955 and follow the download links. Then build Charm++ and AMPI from source.
1956
1957 The build script for Charm++ is called ``build``. The syntax for this
1958 script is:
1959
1960 .. code-block:: bash
1961
1962    $ build <target> <version> <opts>
1963
1964 For building AMPI (which also includes building Charm++ and other
1965 libraries needed by AMPI), specify ``<target>`` to be ``AMPI``. And
1966 ``<opts>`` are command line options passed to the ``charmc`` compile
1967 script. Common compile time options such as
1968 ``-g, -O, -Ipath, -Lpath, -llib`` are accepted.
1969
1970 To build a debugging version of AMPI, use the option: ``-g``. To build a
1971 production version of AMPI, use the option: ``-with-production``.
1972
1973 ``<version>`` depends on the machine, operating system, and the
1974 underlying communication library one wants to use for running AMPI
1975 programs. See the charm/README file for details on picking the proper
1976 version. Here is an example of how to build a debug version of AMPI in a
1977 linux and ethernet environment:
1978
1979 .. code-block:: bash
1980
1981    $ ./build AMPI netlrts-linux-x86_64 -g
1982
1983 And the following is an example of how to build a production version of
1984 AMPI on a Cray XC system, with MPI-level error checking in AMPI turned
1985 off:
1986
1987 .. code-block:: bash
1988
1989    $ ./build AMPI gni-crayxc --with-production --disable-ampi-error-checking
1990
1991 AMPI can also be built with support for shared memory on any
1992 communication layer by adding "smp" as an option after the build target.
1993 For example, on an Infiniband Linux cluster:
1994
1995 .. code-block:: bash
1996
1997    $ ./build AMPI verbs-linux-x86_64 smp --with-production
1998
1999 AMPI ranks are implemented as user-level threads with a stack size
2000 default of 1MB. If the default is not correct for your program, you can
2001 specify a different default stack size (in bytes) at build time. The
2002 following build command illustrates this for an Intel Omni-Path system:
2003
2004 .. code-block:: bash
2005
2006    $ ./build AMPI ofi-linux-x86_64 --with-production -DTCHARM_STACKSIZE_DEFAULT=16777216
2007
2008 The same can be done for AMPI’s RDMA messaging threshold using
2009 ``AMPI_RDMA_THRESHOLD_DEFAULT`` and, for messages sent within the same
2010 address space (ranks on the same worker thread or ranks on different
2011 worker threads in the same process in SMP builds), using
2012 ``AMPI_SMP_RDMA_THRESHOLD_DEFAULT``. Contiguous messages with sizes
2013 larger than the threshold are sent via RDMA on communication layers that
2014 support this capability. You can also set the environment variables
2015 ``AMPI_RDMA_THRESHOLD`` and ``AMPI_SMP_RDMA_THRESHOLD`` before running a
2016 job to override the default specified at build time.
2017
2018 Building and Running AMPI Programs
2019 ==================================
2020
2021 Building AMPI Programs
2022 ----------------------
2023
2024 AMPI provides a compiler called *ampicc* in your charm/bin/ directory.
2025 You can use this compiler to build your AMPI program the same way as
2026 other compilers like cc. All the command line flags that you would use
2027 for other compilers can be used with the AMPI compilers the same way.
2028 For example:
2029
2030 .. code-block:: bash
2031
2032    $ ampicc -c pgm.c -O3
2033    $ ampif90 -c pgm.f90 -O0 -g
2034    $ ampicc -o pgm pgm.o -lm -O3
2035
2036 To use Isomalloc for transparently migrating user heap data, link with
2037 *-memory isomalloc*. To use a Charm++ load balancer, link a strategy or
2038 a suite of strategies in with *-module <LB>*. For example:
2039
2040 .. code-block:: bash
2041
2042    $ ampicc pgm.c -o pgm -O3 -memory isomalloc -module CommonLBs
2043
2044 Running AMPI programs
2045 ---------------------
2046
2047 AMPI offers two options to execute an AMPI program, ``charmrun`` and
2048 ``ampirun``.
2049
2050 Running with charmrun
2051 ~~~~~~~~~~~~~~~~~~~~~
2052
2053 The Charm++ distribution contains a script called ``charmrun`` that
2054 makes the job of running AMPI programs portable and easier across all
2055 parallel machines supported by Charm++. ``charmrun`` is copied to a
2056 directory where an AMPI program is built using ``ampicc``. It takes a
2057 command line parameter specifying number of processors, and the name of
2058 the program followed by AMPI options (such as number of ranks to create,
2059 and the stack size of every user-level thread) and the program
2060 arguments. A typical invocation of an AMPI program ``pgm`` with
2061 ``charmrun`` is:
2062
2063 .. code-block:: bash
2064
2065    $ ./charmrun +p16 ./pgm +vp64
2066
2067 Here, the AMPI program ``pgm`` is run on 16 physical processors with 64
2068 total virtual ranks (which will be mapped 4 per processor initially).
2069
2070 To run with load balancing, specify a load balancing strategy. If
2071 Address Space Layout Randomization is enabled on your target system, you
2072 may need to add the flag ``+isomalloc_sync`` when running with
2073 migration. You can also specify the size of user-level thread’s stack
2074 using the ``+tcharm_stacksize`` option, which can be used to decrease
2075 the size of the stack that must be migrated, as in the following
2076 example:
2077
2078 .. code-block:: bash
2079
2080    $ ./charmrun +p16 ./pgm +vp128 +tcharm_stacksize 32K +balancer RefineLB
2081
2082 Running with ampirun
2083 ~~~~~~~~~~~~~~~~~~~~
2084
2085 For compliance with the MPI standard and simpler execution, AMPI ships
2086 with the ``ampirun`` script that is similar to ``mpirun`` provided by
2087 other MPI runtimes. As with ``charmrun``, ``ampirun`` is copied
2088 automatically to the program directory when compiling an application
2089 with ``ampicc``.
2090
2091 The basic usage of ampirun is as follows:
2092
2093 .. code-block:: bash
2094
2095    $ ./ampirun -np 16 --host h1,h2,h3,h4 ./pgm
2096
2097 This command will create 16 (non-virtualized) ranks and distribute them
2098 on the hosts h1-h4.
2099
2100 When using the ``-vr`` option, AMPI will create the number of ranks
2101 specified by the ``-np`` parameter as virtual ranks, and will create
2102 only one process per host:
2103
2104 .. code-block:: bash
2105
2106    $ ./ampirun -np 16 --host h1,h2,h3,h4 -vr ./pgm
2107
2108 Other options (such as the load balancing strategy), can be specified in
2109 the same way as for charmrun:
2110
2111 .. code-block:: bash
2112
2113    $ ./ampirun -np 16 ./pgm +balancer RefineLB
2114
2115 Other options
2116 ~~~~~~~~~~~~~
2117
2118 Note that for AMPI programs compiled with gfortran, users may need to
2119 set the following environment variable to see program output to stdout:
2120
2121 .. code-block:: bash
2122
2123    $ export GFORTRAN_UNBUFFERED_ALL=1
2124
2125 .. [1]
2126    Currently, AMPI supports the MPI-2.2 standard, and the MPI-3.1
2127    standard is under active development, though we already support
2128    non-blocking and neighborhood collectives among other MPI-3.1
2129    features.
2130
2131 .. [2]
2132    http://www-unix.mcs.anl.gov/romio/
2133
2134 .. [3]
2135    http://www.eclipse.org/photran
2136
2137 .. [4]
2138    http://rosecompiler.org/
2139
2140 .. [5]
2141    Currently, we assume that the interface code, which does mapping and
2142    interpolation among the boundary values of Fluids and Solids domain,
2143    is integrated with one of Fluids and Solids.
2144
2145 .. [PiP2018]
2146    Atsushi Hori, Min Si, Balazs Gerofi, Masamichi Takagi, Jai Dayal, Pavan
2147    Balaji, and Yutaka Ishikawa. 2018. Process-in-process: techniques for
2148    practical address-space sharing.  In Proceedings of the 27th
2149    International Symposium on High-Performance Parallel and Distributed
2150    Computing (HPDC '18). ACM, New York, NY, USA,  131-143. DOI:
2151    https://doi.org/10.1145/3208040.3208045