/**
* Copyright (C) Mellanox Technologies Ltd. 2001-2015. ALL RIGHTS RESERVED.
*
* See file LICENSE for terms.
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <string.h>
#include <getopt.h>
#include <limits.h>
#include <shmem.h>
#define GLOBAL_DATA_SIZE 1024
static int show_result(const struct timeval *tv_prev,
const struct timeval *tv_curr,
long iters, size_t msg_size, int force)
{
double elapsed;
elapsed = (tv_curr->tv_sec + tv_curr->tv_usec * 1e-6) -
(tv_prev->tv_sec + tv_prev->tv_usec * 1e-6);
if (((elapsed >= 1.0) || force) && (shmem_my_pe() == 0)) {
printf("%ld iterations, %lu bytes, latency: %.3f usec\n",
iters, msg_size, elapsed * 1e6 / iters / 2.0);
return 1;
}
return 0;
}
static void run_pingpong(char *mem, size_t msg_size, long num_iters, int use_wait,
int do_quiet, int use_flag)
{
struct timeval tv_prev, tv_curr;
int my_pe, dst_pe;
volatile int *rsn;
char *msg;
int *ssn;
int sn;
long i, prev_i;
msg = malloc(msg_size);
if (msg == NULL) {
return;
}
memset(msg, 0, msg_size);
gettimeofday(&tv_prev, NULL);
prev_i = 0;
my_pe = shmem_my_pe();
dst_pe = 1 - my_pe;
rsn = (int*)&mem[msg_size - sizeof(int)];
ssn = (int*)&msg[msg_size - sizeof(int)];
for (i = 0; i < num_iters; ++i) {
sn = i & 127;
*ssn = sn;
if (my_pe == 0) {
shmem_putmem(mem, msg, msg_size, dst_pe);
if (do_quiet) {
shmem_quiet();
}
}
if (use_wait) {
shmem_int_wait_until(rsn, SHMEM_CMP_EQ, sn);
} else {
while (*rsn != sn);
}
if (my_pe == 1) {
if (use_flag) {
shmem_putmem(mem, msg, msg_size - sizeof(int), dst_pe);
shmem_fence();
shmem_int_put((int*)rsn, ssn, 1, dst_pe);
} else {
shmem_putmem(mem, msg, msg_size, dst_pe);
}
if (do_quiet) {
shmem_quiet();
}
}
if ((i % 1000) == 0) {
gettimeofday(&tv_curr, NULL);
if (show_result(&tv_prev, &tv_curr, i - prev_i, msg_size, 0)) {
prev_i = i;
tv_prev = tv_curr;
}
}
}
gettimeofday(&tv_curr, NULL);
show_result(&tv_prev, &tv_curr, num_iters - prev_i, msg_size, 1);
free(msg);
}
static void usage()
{
printf("Usage: shmem_pingpong [options]\n");
printf("\n");
printf("Options are:\n");
printf(" -n <iters> Specify number of iterations to run (default: 10000).\n");
printf(" -s <size> Specify message size (default: 4 bytes).\n");
printf(" -w Wait for data using shmem_wait_until() (default: poll on memory).\n");
printf(" -f Send data and flag separately with shmem_fence() in-between.\n");
printf(" -g Use global data (default: heap).\n");
printf(" -q call shmem_quiet() after every shmem_put().\n");
printf("\n");
}
int main(int argc, char **argv)
{
static char global_buffer[GLOBAL_DATA_SIZE];
int use_wait, use_global, do_quiet, use_flag;
size_t msg_size;
long num_iters;
int my_pe;
char *mem;
int c;
start_pes(0);
my_pe = shmem_my_pe();
if (shmem_n_pes() != 2) {
fprintf(stderr, "This test requires exactly 2 processes\n");
return -1;
}
num_iters = 10000;
use_global = 0;
use_wait = 0;
do_quiet = 0;
use_flag = 0;
msg_size = 8;
while ((c = getopt (argc, argv, "n:s:wgqfh")) != -1) {
switch (c) {
break;
case 'n':
num_iters = atol(optarg);
if (num_iters == 0) {
num_iters = LONG_MAX;
}
break;
case 'w':
use_wait = 1;
break;
case 'g':
use_global = 1;
break;
case 'q':
do_quiet = 1;
break;
case 'f':
use_flag = 1;
break;
case 's':
msg_size = atol(optarg);
break;
case 'h':
default:
if (my_pe == 0) {
usage();
}
return 0;
}
}
if (msg_size < sizeof(int)) {
fprintf(stderr, "message size must be at least %lu\n", sizeof(int));
return -1;
}
if (use_global) {
if (msg_size <= GLOBAL_DATA_SIZE) {
mem = global_buffer;
} else {
fprintf(stderr, "global data can be used only up to %lu bytes\n",
(size_t)GLOBAL_DATA_SIZE);
return -1;
}
} else {
mem = shmalloc(msg_size);
}
memset(mem, 0xff, msg_size);
shmem_barrier_all();
run_pingpong(mem, msg_size, num_iters, use_wait, do_quiet, use_flag);
shmem_barrier_all();
if (!use_global) {
shfree(mem);
}
shmem_finalize();
return 0;
}