6.2. NETWORKS


Parallel Computing on Heterogeneous Networks, by Alexey Lastovetsky
ISBN 0-471-22982-2 Copyright 2003 by John Wiley & Sons, Inc.

6.1. FIRST MPC PROGRAMS

A parallel mpC program is a set of parallel processes interacting (i.e., synchronizing their work and transferring data) by means of message passing. The mpC programmer cannot determine how many processes make up the program and which computers execute which processes. This is specified by some means external to the mpC language. The source mpC code determines only which process of the program performs which computations.

To begin, consider a simple program that does the same thing as the most famous C program, namely which is to output the text “Hello, world!” to the user’s terminal:

#include <stdio.h> int [*]main() {    [host]printf("Hello, world.\n"); }

The code of this mpC program differs very little from the code of the C program. The first difference is the [*] construct before the name main in the definition of the main function. This specifies the kind of the function and shows that code of this function will be executed by all processes of the parallel program. Functions similar to the function main are called basic functions in mpC. A basic function works correctly only if all processes of the parallel program call it. The mpC compiler controls the correctness of the basic function calls.

The second difference is construct [host] before the function name printf in the expression where this standard C library function is called. Unlike function main, function printf does not need to be called in parallel by all processes of the parallel program in order to work correctly.

Moreover a call to the function in any single process of the parallel program makes sense and is correct. Such functions are called nodal in mpC.

The mpC language allows any single process of the parallel program to call a nodal function. Any group of processes is allowed to call a nodal function in parallel. In the program above, a call to function printf is only executed by a single process of the parallel program. Namely it is executed by a process that is associated with the user’s terminal, which is the terminal from which the program was started. The mpC keyword host is always associated with this very process. So, in the execution of the program, all processes do nothing except that the host-process calls function printf.

The syntax of the next program

#include <stdio.h> int [*]main() {    printf("Hello, world.\n"); }

differs even less from the syntax of the C “Hello, world!” program. Nevertheless, it describes more parallel computations than the previous mpC program. Namely, in its execution, all processes of the parallel program call function printf.

The result of this program depends on the operating environment. In some environments, the standard output of all the parallel processes will go to the user’s terminal, from which the program was started. Then the user will see as many greetings “Hello, world!” on the terminal as many parallel processes will constitute the program—one greeting from each process. In other environments, the user will only see the greetings from the processes, running on the same computer as the host-process, or even a single greeting from the host-process.

Thus the program above may produce different results in different environments. This means that the program is not portable. This is a serious disadvantage of the program.

The next program outputs the greeting “Hello, world!” to the user’s terminal from all processes of the parallel program:

#include <mpc.h> int [*]main() {    MPC_Printf("Hello, world.\n"); }

Unlike the previous program, this program is portable. The mpC library function MPC_Printf guarantees that each process that calls this function outputs “Hello, world!” right to the user’s terminal.

The third mpC program only differs from our first one by a richer content message, which the host-process sends to the user’s terminal. Again, all of the other processes do nothing:

#include <stdio.h> #include <sys/utsname.h> int [*]main() {    struct utsname [host]un;    [host]uname(&un);    [host]printf("Hello world! Host-process runs on \"%s\".\n",                  un.nodename); }

Note that, in addition to “Hello, world!” the host-process outputs the name of the computer that runs that process. To initiate this, we define variable un, which is allocated in the memory of the host-process (specified by the construct [host] before the name of this variable in the definition). After the host-process calls the nodal library function uname, member nodename of structure un will contain a pointer to the name of the computer running the process.

The next program sends messages with richer content to the user’s terminal from all processes of the parallel program:

#include <mpc.h> #include <sys/utsname.h> int [*]main() {    struct utsname un;    uname(&un);    MPC_Printf("Hello world! I’m on \"%s\".\n",                un.nodename); }

In addition to greeting “Hello, world!” each process informs of the name of the computer that runs the process. This is because the distributed variable un is defined in the program. The variable is called distributed because each process of the parallel program holds a copy of this variable in its memory. Therefore the region of storage, represented by this variable, is distributed over the processes. Actually the distributed variable un is nothing more than a set of normal (undistributed) variables, each of which is called a projection of this distributed variable onto the corresponding process.

After each process of this parallel program calls function uname, member nodename of the corresponding projection of the distributed structure un will provide a pointer to the name of the computer, which runs the process. The value of a distributed variable or distributed expression, such as un.nodename or &un, is distributed over processes of the parallel program in natural way, and is called a distributed value.

The preceding mpC program depends on the Unix utsname library in obtaining the names of the computing nodes in the network. Therefore the program can be compiled and executed only on networks consisting of Unix computers. The next mpC program has the same functionality but provides more portability:

#include <mpc.h> #include <stdlib.h> int [*]main() {    char *nodename;    int namelen;    MPC_Processor_name(&nodename, &namelen);    MPC_Printf("Hello world! I’m on \"%s\".\n",                nodename);    free(nodename); }

The program uses the mpC nodal library function MPC_Processor_name to get the name of the processor on which it is called. The name is returned as a character string. The function also returns the number of characters in the string. This program can be compiled and executed on networks of computers running different operating systems, and not only different clones of Unix. The function MPC_Processor_name allocates memory for the name of the processor, and it is the responsibility of the programmer to free the memory as soon as the name is no longer needed.

The next program extends the output of the preceding program with information about the total number of processes of the parallel program:

#include <mpc.h> #include <stdlib.h> int [*]main() {    char *nodename;    int namelen;    repl int one;    repl int number_of_processes;    MPC_Processor_name(&nodename, &namelen);    one = 1;    number_of_processes = one[+];    MPC_Printf("Hello world! I’m one of %d processes"       "and run on \"%s\".\n", number_of_processes,       nodename);    free(nodename); }

The program defines two integer distributed variables, one and number_of_processes. All projections of variable one are assigned 1 as a result of evaluation of assignment one=1.

The result of applying the postfix reduction operator [+] to variable one will be a distributed value whose projection to any process will be equal to the sum of values of all projections of variable one. In other words, the projection of the value of expression one[+] to any process of the parallel program will be equal to the total number of processes. After the distributed value is assigned to the distributed variable number_of_processes, all of the projections of this variable will hold that value, which is the total number of processes of the parallel program.

The definition of the distributed variable one contains the mpC keyword repl (a short form of replicated). It informs the compiler that all projections of the value of the variable should be equal to each other in any expression of the program. Such distributed variables are called replicated in mpC (correspondingly, the value of a replicated variable is called a replicated value).

Replicated variables and expressions play an important role in mpC. The mpC compiler checks the property “to be replicated” declared by the programmer and warns about its possible violations.

Note that a simpler program,

#include <mpc.h> #include <stdlib.h> int [*]main() {    char *nodename;    int namelen;    MPC_Processor_name(&nodename, &namelen);    MPC_Printf("Hello world! I’m one of %d processes"       "and run on \"%s\".\n", MPC_Total_nodes(),       nodename);    free(nodename); } 

enables the same result as the previous program by using the mpC library function MPC_Total_nodes, which just returns the total number of processes of the parallel program. Besides, this program is more efficient because, unlike the evaluation of expression one[+], a parallel call to function MPC_Total_nodes does not need data transfer between processes of the program.

In comparison the next program is less efficient:

#include <mpc.h> #include <stdlib.h> int [*]main() {    char *nodename;    int namelen;    int [host]local_one;    repl int one;    repl int number_of_processes;    MPC_Processor_name(&nodename, &namelen);    local_one = 1;    one = local_one;    number_of_processes = one[+];    MPC_Printf("Hello world! I’m one of %d processes"       "and run on \"%s\".\n", number_of_processes,       nodename);    free(nodename); }

However, this program does demonstrate how assignment can be used to specify transferring data between processes of the mpC program. Variable local_one is allocated in memory of the host-process and initialized by 1. Variable one is replicated across processes of the parallel program. Execution of assignment one=local_one first broadcasts the value of variable local_one to all processes of the program, and then locally assigns the value to each projection of variable one.




Parallel Computing on Heterogeneous Networks
Parallel Computing on Heterogeneous Networks (Wiley Series on Parallel and Distributed Computing)
ISBN: B000W7ZQBO
EAN: N/A
Year: 2005
Pages: 95

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net