((section 2 "Outdated egg!" (p "This is an egg for CHICKEN 4, the unsupported old release.  You're almost certainly looking for " (int-link "/eggref/5/mpi" "the CHICKEN 5 version of this egg") ", if it exists.") (p "If it does not exist, there may be equivalent functionality provided by another egg; have a look at the " (link "https://wiki.call-cc.org/chicken-projects/egg-index-5.html" "egg index") ". Otherwise, please consider porting this egg to the current version of CHICKEN.") (tags "egg") (toc)) (section 2 "mpi" (p "Message Passing Interface (MPI)")) (section 2 "Documentation" (p (link "http://www.mpi-forum.org/" "MPI") " is a popular standard for distributed-memory parallel programming. It offers both point-to-point message passing and group communication operations (broadcast, scatter/gather, etc).") (p (link "http://www.open-mpi.org/" "Open MPI") " is an implementation of the MPI standard that combines technologies and resources from several other projects (FT-MPI, LA-MPI, LAM/MPI, and PACX-MPI) in order to build the best MPI library available.") (p "The Chicken MPI egg provides a Scheme interface to a large subset of the MPI 1.2 procedures for communication.  It is based on the " (link "http://forge.ocamlcore.org/projects/ocamlmpi/" "Ocaml MPI") " library by Xavier Leroy. The " (tt "mpi") " library has been tested with Open MPI versions 1.2.4 - 1.10.1.") (section 3 "Initialization and time procedures" (def (sig (procedure "MPI:init :: [ARG1 ...] -> UNDEFINED" (id MPI:init))) (p "Initializes the MPI execution environment. This routine must be called before any other MPI routine. MPI can be initialized at most once.")) (def (sig (procedure "MPI:spawn :: COMMAND * ARGUMENTS *  MAXPROCS * LOCATIONS * ROOT * COMM -> (COMM * S32VECTOR)" (id MPI:spawn))) (p "Spawns " (tt "MAXPROCS") " identical copies of the MPI program specified by " (tt "COMMAND") " and returns an intercommunicator and a vector of status values. " (tt "ARGUMENTS") " is a list of command-line arguments. " (tt "LOCATIONS") " is a list of string pairs " (tt "(HOST * WDIR)") " that tell MPI the host and working directory where to start processes.")) (def (sig (procedure "MPI:finalize" (id MPI:finalize))) (p "Terminates the MPI execution environment.")) (def (sig (procedure "MPI:wtime :: VOID -> SECONDS" (id MPI:wtime))) (p "Returns the number of seconds representing elapsed wall-clock time on the calling process."))) (section 3 "Handling of communicators" (def (sig (procedure "MPI:comm? :: OBJ -> BOOL" (id MPI:comm?))) (p "Returns true if " (tt "OBJ") " is an MPI communicator object, false otherwise.")) (def (sig (procedure "MPI:get-comm-world:: VOID -> COMM" (id MPI:get-comm-world))) (p "Returns the default communicator created by " (tt "MPI_Init") "; the group associated with this communicator contains all processes.")) (def (sig (procedure "MPI:comm-size :: COMM -> INTEGER" (id MPI:comm-size))) (p "Returns the size of the group associated with communicator " (tt "COMM") ".")) (def (sig (procedure "MPI:comm-rank :: COMM -> INTEGER" (id MPI:comm-rank))) (p "Returns the rank of the calling process in communicator " (tt "COMM") ".")) (def (sig (procedure "MPI:comm-equal? :: COMM1 * COMM2 -> BOOL" (id MPI:comm-equal?))) (p "Returns true if the two given communicators are for identical groups, false otherwise.")) (def (sig (procedure "MPI:comm-split :: COMM * COLOR * KEY -> COMM" (id MPI:comm-split))) (p "Creates new communicators based on colors and keys.")) (def (sig (procedure "MPI:comm-create :: COMM * GROUP -> COMM" (id MPI:comm-create))) (p "Creates a new communicator with communication group that spans all processes in " (tt "GROUP") " and a new context. See the procedures in subsection " (i "Handling of communication groups") " for information on how to create process group objects.")) (def (sig (procedure "MPI:make-cart :: COMM * DIMS * PERIODS * REORDER -> COMM" (id MPI:make-cart))) (p "Creates a new communicator with Cartesian topology information. Argument " (tt "DIMS") " is an SRFI-4 s32vector that contains the number of dimensions of the Cartesian grid. Argument " (tt "PERIODS") " is an SRFI-4 s32vector of the same length as " (tt "DIMS") " that indicates if the grid is periodic (1) or not (0) in each dimension. Argument " (tt "REORDER") " is a boolean value that indicates whether process ranking may be reordered.")) (def (sig (procedure "MPI:make-dims :: NNODES * NDIMS -> DIMS" (id MPI:make-dims))) (p "Creates a division of processes in a Cartesian grid. Argument " (tt "NNODES") " is the number of nodes in the grid. Argument " (tt "NDIMS") " is the number of Cartesian dimensions. The return values is an SRFI-4 s32vector.")) (def (sig (procedure "MPI:cart-coords :: COMM * RANK -> COORDS" (id MPI:cart-coords))) (p "Determines process coordinates in Cartesian topology, given a rank in the group. The return value is an SRFI-4 s32vector of length " (tt "NDIMS") " (the number of dimensions in the Cartesian topology)."))) (section 3 "Handling of communication groups" (def (sig (procedure "MPI:group? :: OBJ -> BOOL" (id MPI:group?))) (p "Returns true if " (tt "OBJ") " is an MPI group object, false otherwise.")) (def (sig (procedure "MPI:comm-group :: COMM -> GROUP" (id MPI:comm-group))) (p "Returns the group associated with the given communicator.")) (def (sig (procedure "MPI:group-size :: GROUP -> INTEGER" (id MPI:group-size))) (p "Returns the size of the group " (tt "GROUP") ".")) (def (sig (procedure "MPI:group-rank :: GROUP -> INTEGER" (id MPI:group-rank))) (p "Returns the rank of the calling process in the given group.")) (def (sig (procedure "MPI:group-translate-ranks :: GROUP1 * RANKS * GROUP2 -> RANKS2" (id MPI:group-translate-ranks))) (p "Translates the ranks of processes in one group to those in another group. The return value is an SRFI-4 s32vector.")) (def (sig (procedure "MPI:group-union :: GROUP1 * GROUP2 -> GROUP" (id MPI:group-union)))) (def (sig (procedure "MPI:group-difference :: GROUP1 * GROUP2 -> GROUP" (id MPI:group-difference)))) (def (sig (procedure "MPI:group-intersection :: GROUP1 * GROUP2 -> GROUP" (id MPI:group-intersection)))) (def (sig (procedure "MPI:group-incl :: GROUP * RANKS -> GROUP" (id MPI:group-incl))) (p "Produces a group by reordering an existing group and taking only members with the given ranks. Argument " (tt "RANKS") " is an SRFI-4 s32vector.")) (def (sig (procedure "MPI:group-excl :: GROUP * RANKS -> GROUP" (id MPI:group-excl))) (p "Produces a group by reordering an existing group and taking only members that do not have the given ranks. Argument " (tt "RANKS") " is an SRFI-4 s32vector."))) (section 3 "MPI datatypes" (def (sig (procedure "MPI:datatype? :: OBJ -> BOOL" (id MPI:datatype?))) (p "Returns true if `OBJ` is an MPI datatype object, false otherwise.")) (def (sig (procedure "MPI:type-extent :: DATATYPE -> (EXTENT LB)" (id MPI:type-extent))) (p "Returns the extent and lower bound of an MPI data type.")) (def (sig (procedure "MPI:type-size :: DATATYPE -> INT" (id MPI:type-size))) (p "Returns the size of a datatype.") (ul (li "MPI:type-char") (li "MPI:type-int") (li "MPI:type-fixnum") (li "MPI:type-flonum") (li "MPI:type-byte") (li "MPI:type-s8") (li "MPI:type-u8") (li "MPI:type-s16") (li "MPI:type-u16") (li "MPI:type-s32") (li "MPI:type-u32") (li "MPI:type-f32") (li "MPI:type-f64")) (p "Predefined MPI data types.")) (def (sig (procedure "MPI:make-type-struct :: FIELD-COUNT * BLOCK-LENS * FIELDTYS -> DATATYPE" (id MPI:make-type-struct))) (p "Given a gield count, field lengths and field types, creates and returns a new MPI structure data type with the given fields."))) (section 3 "Point-to-point communication" (p "Most communication procedures in this library come in several flavors, for fixnums, integers, floating point numbers, bytevectors, and for each of the SRFI-4 homogeneous vector types.") (def (sig (procedure "MPI:send :: DATATYPE * DATA * DEST * TAG * COMM -> UNDEFINED" (id MPI:send)) (procedure "MPI:send-TYPE :: DATA * DEST * TAG * COMM -> UNDEFINED" (id MPI:send-TYPE))) (p "Performs a standard-mode blocking send. Argument " (tt "DEST") " is the rank of the destination process. Argument " (tt "TAG") " is integer message tag. Argument " (tt "DATATYPE") " is an MPI datatype object. " (tt "TYPE") " is one of the following: " (tt "fixnum, int, flonum, bytevector, s8vector, u8vector, s16vector, u16vector, s32vector, u32vector, f32vector, f64vector"))) (def (sig (procedure "MPI:receive :: DATATYPE * SOURCE * TAG * COMM -> DATA" (id MPI:receive)) (procedure "MPI:receive-TYPE :: LENGTH * SOURCE * TAG * COMM -> DATA" (id MPI:receive-TYPE))) (p "Performs a standard-mode blocking receive. Argument " (tt "DEST") " is the rank of the destination process. Argument " (tt "TAG") " is integer message tag. Argument " (tt "LENGTH") " is present only in the vector procedures. " (tt "TYPE") " is one of the following: " (tt "fixnum, int, flonum, bytevector, s8vector, u8vector, s16vector, u16vector, s32vector, u32vector, f32vector, f64vector"))) (def (sig (procedure "MPI:probe :: DATATYPE * SOURCE * TAG * COMM -> (COUNT * SOURCE * TAG)" (id MPI:probe))) (p "Checks for an incoming message. This is a blocking call that returns only after a matching message is found. Argument " (tt "DATATYPE") " is an MPI datatype object. Argument " (tt "SOURCE") " can be " (tt "MPI:any-source") ". Argument " (tt "TAG") " can be " (tt "MPI:any-tag") "."))) (section 3 "Group communication" (def (sig (procedure "MPI:barrier :: COMM -> UNDEFINED" (id MPI:barrier))) (p "Barrier synchronization.")) (def (sig (procedure "MPI:broadcast :: DATATYPE * DATA * ROOT * COMM -> UNDEFINED" (id MPI:broadcast)) (procedure "MPI:broadcast-TYPE :: DATA * ROOT * COMM -> UNDEFINED" (id MPI:broadcast-TYPE))) (p "Broadcasts a message from the process with rank root to all other processes of the group. Argument " (tt "DATATYPE") " is an MPI datatype object. " (tt "TYPE") " is one of the following: " (tt "fixnum, int, flonum, bytevector, s8vector, u8vector, s16vector, u16vector, s32vector, u32vector, f32vector, f64vector"))) (def (sig (procedure "MPI:scatter :: DATATYPE * DATA * SENDCOUNT * ROOT * COMM -> DATA" (id MPI:scatter)) (procedure "MPI:scatter-TYPE :: DATA * SENDCOUNT * ROOT * COMM -> DATA" (id MPI:scatter-TYPE))) (p "Sends data from the root process to all processes in a group, and returns the data received by the calling process. Argument " (tt "SENDCOUNT") " is the number of elements sent to each process. Argument " (tt "DATATYPE") " is an MPI datatype object. Argument " (tt "DATA") " is only required at the root process. All other processes can invoke this procedure with (void) as " (tt "DATA") ". " (tt "TYPE") " is one of the following: " (tt "int, flonum, bytevector, s8vector, u8vector, s16vector, u16vector, s32vector, u32vector, f32vector, f64vector"))) (def (sig (procedure "MPI:scatterv :: DATATYPE * DATA * ROOT * COMM -> DATA" (id MPI:scatterv)) (procedure "MPI:scatterv-TYPE :: DATA * ROOT * COMM -> DATA" (id MPI:scatterv-TYPE))) (p "Sends variable-length data from the root process to all processes in a group, and returns the data received by the calling process.  Argument " (tt "DATATYPE") " is an MPI datatype object. Argument " (tt "DATA") " is only required at the root process, and is a list of values of type " (tt "TYPE") ", where each element of the list is sent to the process of corresponding rank. All other processes can invoke this procedure with (void) as " (tt "DATA") ". " (tt "TYPE") " is one of the following: " (tt "int, flonum, bytevector, s8vector, u8vector, s16vector, u16vector, s32vector, u32vector, f32vector, f64vector"))) (def (sig (procedure "MPI:gather :: DATATYPE * DATA * SENDCOUNT * ROOT * COMM -> DATA" (id MPI:gather)) (procedure "MPI:gather-TYPE :: DATA * SENDCOUNT * ROOT * COMM -> DATA" (id MPI:gather-TYPE))) (p "Gathers data from a group of processes, where each process send data of the same length.  Argument " (tt "SENDCOUNT") " is the number of data elements being sent by each process. Argument " (tt "DATATYPE") " is an MPI datatype object.  " (tt "TYPE") " is one of the following: " (tt "int, flonum, bytevector, s8vector, u8vector, s16vector, u16vector, s32vector, u32vector, f32vector, f64vector"))) (def (sig (procedure "MPI:gatherv :: DATATYPE * DATA * ROOT * COMM -> DATA" (id MPI:gatherv)) (procedure "MPI:gatherv-TYPE :: DATA * ROOT * COMM -> DATA" (id MPI:gatherv-TYPE))) (p "Gathers data from a group of processes, where each process can send data of variable length. Argument " (tt "DATATYPE") " is an MPI datatype object. " (tt "TYPE") " is one of the following: " (tt "int, flonum, bytevector, s8vector, u8vector, s16vector, u16vector, s32vector, u32vector, f32vector, f64vector"))) (def (sig (procedure "MPI:allgather :: DATATYPE * DATA * ROOT * COMM -> DATA" (id MPI:allgather)) (procedure "MPI:allgather-TYPE :: DATA * ROOT * COMM -> DATA" (id MPI:allgather-TYPE))) (p "Gathers data of variable length from all processes and distributes it to all processes. Argument " (tt "DATATYPE") " is an MPI datatype object. " (tt "TYPE") " is one of the following: " (tt "int, flonum, bytevector, s8vector, u8vector, s16vector, u16vector, s32vector, u32vector, f32vector, f64vector"))) (def (sig (procedure "MPI:reduce-TYPE :: DATA * OP * ROOT * COMM -> DATA" (id MPI:reduce-TYPE))) (p "Reduces values on all processes within a group, using a global reduce operation, and return the result at the root process. " (tt "OP") " is one of the following: " (tt "MPI:i_max, MPI:i_min, MPI:i_sum, MPI:i_prod, MPI:i_land, MPI:i_lor, MPI:i_xor") " (integer operations); and " (tt "MPI:f_max, MPI:f_min, MPI:f_sum, MPI:f_prod") " (floating point operations). " (tt "TYPE") " is one of the following: " (tt "int, flonum, bytevector, s8vector, u8vector, s16vector, u16vector, s32vector, u32vector, f32vector, f64vector"))) (def (sig (procedure "MPI:allreduce-TYPE :: DATA * OP * COMM -> DATA" (id MPI:allreduce-TYPE))) (p "Reduces values on all processes within a group, using a global reduce operation, and return the result at each process. " (tt "OP") " is one of the following: " (tt "MPI:i_max, MPI:i_min, MPI:i_sum, MPI:i_prod, MPI:i_land, MPI:i_lor, MPI:i_xor") " (integer operations); and " (tt "MPI:f_max, MPI:f_min, MPI:f_sum, MPI:f_prod") " (floating point operations). " (tt "TYPE") " is one of the following: " (tt "int, flonum, bytevector, s8vector, u8vector, s16vector, u16vector, s32vector, u32vector, f32vector, f64vector"))) (def (sig (procedure "MPI:scan-TYPE :: DATA * OP * COMM -> DATA" (id MPI:scan-TYPE))) (p "Computes a partial reduction across the processes in a group. " (tt "OP") " is one of the following: " (tt "MPI:i_max, MPI:i_min, MPI:i_sum, MPI:i_prod, MPI:i_land, MPI:i_lor, MPI:i_xor") " (integer operations); and " (tt "MPI:f_max, MPI:f_min, MPI:f_sum, MPI:f_prod") " (floating point operations). " (tt "TYPE") " is one of the following: " (tt "int, flonum, bytevector, s8vector, u8vector, s16vector, u16vector, s32vector, u32vector, f32vector, f64vector")))) (section 3 "Round-robin routines" (p "The following variants of " (tt "fold") ", " (tt "map") " and " (tt "for-each") " process lists in round-robin fashion on MPI nodes: for a given node " (tt "n") ", only list elements whose index is a modulo of n will be processed on this node.") (def (sig (procedure "MPI-rr-fold :: FN * INITIAL * XS -> RESULT" (id MPI-rr-fold)))) (def (sig (procedure "MPI-rr-map :: FN * XS -> RESULT" (id MPI-rr-map)))) (def (sig (procedure "MPI-rr-for-each :: FN * XS -> VOID" (id MPI-rr-for-each)))))) (section 2 "Examples" (section 3 "Master/worker example" (highlight scheme ";; Simple master/worker example\n;; Can be run as follows: mpirun -np 4 csi -s master-worker.scm\n;; where -np # indicates the number of processes\n\n(import scheme (chicken base) srfi-4 mpi)\n\n(MPI:init)\n\n;; MPI uses objects called communicators to define how processes\n;; communicate with each other.  Almost all MPI routines require a\n;; communicator as an argument.  \n\n;; `MPI:get-comm-world' returns the communicator object which can send\n;; messages to all running MPI processes\n\n(define comm-world  (MPI:get-comm-world))\n\n;; `MPI:comm-size' returns the number of running MPI processes\n;;  (including the current one)\n\n(define size        (MPI:comm-size comm-world))\n\n;; `MPI:comm-rank' returns the rank of the calling MPI process\n\n(define myrank      (MPI:comm-rank comm-world))\n\n;; We assign rank 0 to be the master process, and the rest will be\n;; worker processes\n\n(if (zero? myrank)\n    (begin\n      (printf \"[~a/~a]: I am the master\\n\" myrank size)\n      (let recur ((i 1))\n        (if (< i size)\n            (begin\n              ;; Send Hello message to process of rank i\n              (MPI:send-bytevector (string->blob (sprintf \"Hello ~a...\" i)) i 0 comm-world)\n              (recur (+ 1 i)))\n            ))\n\n      (let recur ((i 1))\n        (if (< i size)\n             ;; Wait for a response from process of rank i\n            (let ((n (blob->string (MPI:receive-bytevector i MPI:any-tag comm-world))))\n              (printf \"[~a/~a]: received: ~a~%\" myrank size n)\n              (recur (+ 1 i)))\n            ))\n      )\n    (begin\n      (printf \"[~a/~a]: I am a worker\\n\" myrank size)\n      ;; Wait for a message from the master (process 0)\n      (let ((n (blob->string (MPI:receive-bytevector 0 MPI:any-tag comm-world))))\n        (printf \"[~a/~a]: received: ~a\\n\" myrank size n)\n        ;; Send a response back to the master\n        (MPI:send-bytevector (string->blob (sprintf \"Processor ~a reporting!\" myrank))\n                     0 0 comm-world))\n      )\n    )\n\n(MPI:finalize)")) (section 3 "Master/worker implemented with collective operations" (highlight scheme ";; Master/worker example implemented with collective operations\n;; Can be run as follows: mpirun -np 4 csi -s master-worker.scm\n\n(import scheme (chicken base) srfi-1 srfi-4 mpi)\n\n(MPI:init)\n\n;; MPI uses objects called communicators to define how processes\n;; communicate with each other.  Almost all MPI routines require a\n;; communicator as an argument.  \n\n;; `MPI:get-comm-world' returns the communicator object which can send\n;; messages to all running MPI processes\n\n(define comm-world  (MPI:get-comm-world))\n\n;; `MPI:comm-size' returns the number of running MPI processes\n;;  (including the current one)\n\n(define size        (MPI:comm-size comm-world))\n\n;; `MPI:comm-rank' returns the rank of the calling MPI process\n\n(define myrank      (MPI:comm-rank comm-world))\n\n;; We assign rank 0 to be the master process, and the rest will be\n;; worker processes\n\n(if (zero? myrank)\n    (begin\n      (printf \"[~a/~a]: I am the master\\n\" myrank size)\n      \n      ;; data is a list of vectors to be sent to each process.  The\n      ;; master process sends element i from the list to process i\n      ;; (including itself). Note that each process must call scatterv\n      ;; in order to receive its data. In this example, the master\n      ;; ignores the result to its call to scatterv.\n\n      (let ((data (list-tabulate size (lambda (i) (string->blob (sprintf \"Hello ~a...\" i))))))\n        (MPI:scatterv-bytevector data 0 comm-world))\n   \n      ;; With gatherv, each process (master process included) sends\n      ;; the contents of its send buffer to the master process. The\n      ;; master process receives the messages and stores them in rank\n      ;; order.\n\n      (let ((v (MPI:gatherv-bytevector (string->blob \"I am the master!\") 0 comm-world)))\n        (printf \"[~a/~a]: received: ~a\\n\" myrank size (map blob->string v))\n        ))\n    (begin\n      (printf \"[~a/~a]: I am a worker\\n\" myrank size)\n\n      ;; The worker collects its data via a call to scatterv. The data\n      ;; argument is #f because the worker is not sending anything,\n      ;; just receiving.\n\n      (let ((n (blob->string (MPI:scatterv-bytevector #f 0 comm-world))))\n        (printf \"[~a/~a]: received: ~a\\n\" myrank size n)\n\n        ;; The worker sends its result back to the master via a call to gatherv.\n        (MPI:gatherv-bytevector (string->blob (sprintf \"Processor ~a reporting!\" myrank))\n                                0 comm-world))\n      )\n    )\n\n(MPI:finalize)"))) (section 2 "About this egg" (section 3 "Author" (p (int-link "/users/ivan-raikov" "Ivan Raikov"))) (section 3 "Version history" (dl (dt "2.2") (dd "Ported to CHICKEN 5") (dt "2.1") (dd "Support for MPI alltoall / alltoallv operations") (dt "2.0") (dd "Added support for MPI derived datatypes") (dt "1.14") (dd "Added simple round-robin routines") (dt "1.12") (dd "Fixes to allgather-int and allgather-flonum (thanks to Peter Bex)") (dt "1.11") (dd "Test script fixes") (dt "1.9") (dd "Ensure test script returns non-zero on error (thanks to mario)") (dt "1.7") (dd "Switched to wiki documentation") (dt "1.6") (dd "Ported to Chicken 4") (dt "1.5") (dd "Added a binding for MPI:spawn") (dt "1.3") (dd "Bug fix in MPI:scatter-int") (dt "1.2") (dd "Bug fix in the meta file") (dt "1.1") (dd "Bug fixes and improvements to the regression tests") (dt "1.0") (dd "Initial release"))) (section 3 "License" (pre "Copyright 2007-2018 Ivan Raikov.\n\nBased on the Ocaml MPI library by Xavier Leroy. \n\nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or (at\nyour option) any later version.\n\nThis program is distributed in the hope that it will be useful, but\nWITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\nGeneral Public License for more details.\n\nA full copy of the GPL license can be found at\n<http://www.gnu.org/licenses/>."))))