2009-04-30 22:43:26 +04:00
|
|
|
/* -*- C -*-
|
|
|
|
*
|
|
|
|
* Copyright (c) 2008 Los Alamos National Security, LLC. All rights reserved.
|
|
|
|
*
|
2015-02-11 20:26:17 +03:00
|
|
|
* Copyright (c) 2015 Cisco Systems, Inc. All rights reserved.
|
2009-04-30 22:43:26 +04:00
|
|
|
* $COPYRIGHT$
|
2015-06-24 06:59:57 +03:00
|
|
|
*
|
2009-04-30 22:43:26 +04:00
|
|
|
* Additional copyrights may follow
|
2015-06-24 06:59:57 +03:00
|
|
|
*
|
2009-04-30 22:43:26 +04:00
|
|
|
* $HEADER$
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdbool.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <time.h>
|
2015-02-11 20:26:17 +03:00
|
|
|
#include <sys/time.h>
|
2009-04-30 22:43:26 +04:00
|
|
|
|
|
|
|
#include <mpi.h>
|
|
|
|
|
|
|
|
int main(int argc, char* argv[])
|
|
|
|
{
|
|
|
|
int msg;
|
|
|
|
int rank, size, my_twin;
|
|
|
|
int ppn, my_node;
|
|
|
|
struct timeval tv;
|
|
|
|
unsigned long my_timestamp[2];
|
|
|
|
long *timestamps;
|
|
|
|
int i, maxrank;
|
|
|
|
unsigned long maxsec, maxusec, minutes, seconds;
|
|
|
|
unsigned long start_sec, start_usec;
|
|
|
|
float fsecs;
|
|
|
|
int nnodes;
|
|
|
|
bool odd_nnodes;
|
|
|
|
bool recvit;
|
|
|
|
char *ppnstr;
|
2015-06-24 06:59:57 +03:00
|
|
|
|
2009-04-30 22:43:26 +04:00
|
|
|
if (argc < 3) {
|
|
|
|
fprintf(stderr, "start times must be provided\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
ppnstr = getenv("OMPI_COMM_WORLD_LOCAL_SIZE");
|
|
|
|
ppn = strtol(ppnstr, NULL, 10);
|
|
|
|
start_sec = strtol(argv[1], NULL, 10);
|
|
|
|
start_usec = strtol(argv[2], NULL, 10);
|
2015-06-24 06:59:57 +03:00
|
|
|
|
2009-04-30 22:43:26 +04:00
|
|
|
MPI_Init(NULL, NULL);
|
|
|
|
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
|
|
|
|
MPI_Comm_size(MPI_COMM_WORLD, &size);
|
|
|
|
|
|
|
|
/* this program requires that the size be an integer multiple of ppn */
|
|
|
|
if (0 != (size % ppn)) {
|
|
|
|
if (0 == rank) {
|
|
|
|
fprintf(stderr, "The number of procs must be an integer multiple of the ppn\n"
|
|
|
|
"Given: num_procs %d ppn %d\n", size, ppn);
|
|
|
|
MPI_Abort(MPI_COMM_WORLD, 1);
|
|
|
|
} else {
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
}
|
2015-06-24 06:59:57 +03:00
|
|
|
|
2009-04-30 22:43:26 +04:00
|
|
|
/* see how many nodes we have */
|
|
|
|
nnodes = size / ppn;
|
2015-06-24 06:59:57 +03:00
|
|
|
|
2009-04-30 22:43:26 +04:00
|
|
|
odd_nnodes = false;
|
|
|
|
if (0 != (nnodes % 2)) {
|
|
|
|
/* we have an odd # of nodes */
|
|
|
|
odd_nnodes = true;
|
|
|
|
}
|
2015-06-24 06:59:57 +03:00
|
|
|
|
2009-04-30 22:43:26 +04:00
|
|
|
/* compute the rank of the rank with which I am to exchange a message.
|
|
|
|
* Per requirements, this proc must be on another node. To accomplish
|
|
|
|
* this with max efficiency, we take advantage of knowing that the ppn
|
|
|
|
* on every node will be the same. We therefore pair up the nodes, and
|
|
|
|
* pair up the procs on each node, so that only one connection is setup
|
|
|
|
* for each proc. We also want to ensure that the node pairs are
|
|
|
|
* "neighboring" - i.e., that they hopefully share a switch so that the
|
|
|
|
* hop count of sending the messages is minimized.
|
|
|
|
*/
|
2015-06-24 06:59:57 +03:00
|
|
|
|
2009-04-30 22:43:26 +04:00
|
|
|
/* first, determine if my node is odd or even */
|
|
|
|
my_node = rank / ppn;
|
2015-06-24 06:59:57 +03:00
|
|
|
|
2009-04-30 22:43:26 +04:00
|
|
|
if (0 != (my_node % 2)) {
|
|
|
|
/* compute my twin's rank - as I am an odd numbered node, my
|
|
|
|
* twin will be on the node below me. Thus, its rank will be
|
|
|
|
* my rank - ppn
|
|
|
|
*/
|
|
|
|
my_twin = rank - ppn;
|
|
|
|
/* if I am an odd numbered node, then I will receive first */
|
|
|
|
MPI_Recv(&msg, 1, MPI_INT, my_twin, 1, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
|
|
|
|
/* receive the return message so that we meet the stated requirement
|
|
|
|
* that -every- proc send a message
|
|
|
|
*/
|
|
|
|
MPI_Send(&msg, 1, MPI_INT, my_twin, 1, MPI_COMM_WORLD);
|
|
|
|
} else {
|
|
|
|
/* compute my twin's rank - as I am an even numbered node, my
|
|
|
|
* twin will be on the node above me. Thus, its rank will be
|
|
|
|
* my rank + ppn
|
|
|
|
*/
|
|
|
|
my_twin = rank + ppn;
|
|
|
|
/* if we have an odd number of nodes, then the last node will be
|
|
|
|
* even and will have no one above them. In this case, we wrap around
|
|
|
|
* and ask that node=0 take the additional connections
|
|
|
|
*/
|
|
|
|
recvit = true;
|
|
|
|
if (my_twin >= size) {
|
|
|
|
my_twin = my_twin - size;
|
|
|
|
recvit = false;
|
|
|
|
}
|
|
|
|
/* I am an even numbered node, so I send first */
|
|
|
|
MPI_Send(&msg, 1, MPI_INT, my_twin, 1, MPI_COMM_WORLD);
|
|
|
|
/* now receive the reply so my twin also meets the requirement - but only
|
|
|
|
* if we don't have an odd number of nodes. If we have an odd number of
|
|
|
|
* nodes, then the node=0 procs will already have met their requirement
|
|
|
|
*/
|
|
|
|
if (recvit) {
|
|
|
|
MPI_Recv(&msg, 1, MPI_INT, my_twin, 1, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
|
|
|
|
}
|
|
|
|
}
|
2015-06-24 06:59:57 +03:00
|
|
|
|
2009-04-30 22:43:26 +04:00
|
|
|
/* if we have an odd number of nodes and I am on node=0, then I have
|
|
|
|
* to take the extra recv
|
|
|
|
*/
|
|
|
|
if (odd_nnodes && 0 == my_node) {
|
|
|
|
my_twin = size - ppn + rank;
|
|
|
|
MPI_Recv(&msg, 1, MPI_INT, my_twin, 1, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
|
|
|
|
}
|
2015-06-24 06:59:57 +03:00
|
|
|
|
2009-04-30 22:43:26 +04:00
|
|
|
/* get a completion time stamp */
|
|
|
|
gettimeofday(&tv, NULL);
|
|
|
|
my_timestamp[0] = tv.tv_sec;
|
|
|
|
my_timestamp[1] = tv.tv_usec;
|
2015-06-24 06:59:57 +03:00
|
|
|
|
2009-04-30 22:43:26 +04:00
|
|
|
/* THIS COMPLETES THE OFFICIAL TIMING POINT */
|
|
|
|
|
|
|
|
/* Gather to get all the timestamps to rank 0 */
|
|
|
|
timestamps = NULL;
|
|
|
|
if (0 == rank) {
|
|
|
|
timestamps = malloc(2 * size * sizeof(unsigned long));
|
|
|
|
if (NULL == timestamps) {
|
|
|
|
MPI_Abort(MPI_COMM_WORLD, 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
MPI_Gather(&my_timestamp, 2, MPI_LONG,
|
|
|
|
timestamps, 2, MPI_LONG, 0, MPI_COMM_WORLD);
|
|
|
|
if (0 == rank) {
|
|
|
|
/* The "timestamps" array will now have everyone's timestamp
|
|
|
|
(i.e., rank 0's timestamp will be in pos 0 & 1,, rank 1's timestamp
|
|
|
|
will be in 2 & 3, ...etc. */
|
|
|
|
/* find the maximum timestamp */
|
|
|
|
maxsec = start_sec;
|
|
|
|
maxusec = start_usec;
|
|
|
|
maxrank = -1;
|
|
|
|
for (i=0; i < 2*size; i+=2) {
|
|
|
|
if (timestamps[i] < maxsec) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (timestamps[i] == maxsec &&
|
|
|
|
timestamps[i+1] < maxusec) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
maxsec = timestamps[i];
|
|
|
|
maxusec = timestamps[i+1];
|
|
|
|
maxrank = i/2;
|
|
|
|
}
|
|
|
|
free(timestamps);
|
|
|
|
/* subtract starting time to get time in microsecs for test */
|
|
|
|
maxsec = maxsec - start_sec;
|
|
|
|
if (maxusec >= start_usec) {
|
|
|
|
maxusec = maxusec - start_usec;
|
|
|
|
} else {
|
|
|
|
maxsec--;
|
|
|
|
maxusec = 1000000 - start_usec + maxusec;
|
|
|
|
}
|
|
|
|
/* pretty-print the result */
|
|
|
|
seconds = maxsec + (maxusec / 1000000l);
|
|
|
|
minutes = seconds / 60l;
|
|
|
|
seconds = seconds % 60l;
|
|
|
|
if (0 == minutes && 0 == seconds) {
|
|
|
|
fsecs = ((float)(maxsec)*1000000.0 + (float)maxusec) / 1000.0;
|
|
|
|
fprintf(stderr, "Time test was completed in %8.2f millisecs\nSlowest rank: %d\n",
|
|
|
|
fsecs, maxrank);
|
|
|
|
} else {
|
|
|
|
fprintf(stderr, "Time test was completed in %3lu:%02lu min:sec\nSlowest rank: %d\n",
|
|
|
|
minutes, seconds, maxrank);
|
|
|
|
}
|
|
|
|
}
|
2015-06-24 06:59:57 +03:00
|
|
|
|
2009-04-30 22:43:26 +04:00
|
|
|
cleanup:
|
|
|
|
/* this completes the test */
|
|
|
|
MPI_Finalize();
|
2015-06-24 06:59:57 +03:00
|
|
|
|
2009-04-30 22:43:26 +04:00
|
|
|
return 0;
|
|
|
|
}
|