/* $Id: ping-pong.c 1343 2006-12-04 16:07:31Z olau $ */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <mpi.h>
#include "../../globaldefs.h"

#define ROOT                (0)
#define MAX_ELEMENTS        (128*1024*1024)
#define NUM_ITERATIONS      (100)


void prefix(int i, int *msg_size, char *mult)
{
  if (i >= 1024*1024*1024) {
    *msg_size = sizeof(BYTE) * i / (1024*1024/1024);
    *mult = 'G';
  }
  else if (i >= 1024*1024) {
    *msg_size = sizeof(BYTE) * i / (1024*1024);
    *mult = 'M';
  }
  else if (i >= 1024) {
    *msg_size = sizeof(BYTE) * i / 1024;
    *mult = 'K';
  }
  else {
    *msg_size = sizeof(BYTE) * i;
    *mult = ' ';
  }
}


int main(int argc, char *argv[])
{
  int myrank;
  int size;
  MPI_Status stat;
  int i, j;
  double t;
  BYTE *buf;
  int msg_size;
  char mult;

  MPI_Init(&argc, &argv);
  MPI_Comm_size(MPI_COMM_WORLD, &size);
  MPI_Comm_rank(MPI_COMM_WORLD, &myrank);

  if (size != 2) {
    if (myrank == ROOT)
      printf("Fehler: Das Programm muss auf genau zwei Prozessoren laufen.\n");
    MPI_Abort(MPI_COMM_WORLD, EXIT_FAILURE);
  }

  buf = (BYTE *) malloc(MAX_ELEMENTS * sizeof(BYTE));
  if (buf == NULL) {
    if (myrank == ROOT)
      printf("Fehler: Nicht genügend Speicher.\n");
    MPI_Abort(MPI_COMM_WORLD, EXIT_FAILURE);
  }

  memset(buf, 0, MAX_ELEMENTS * sizeof(BYTE));

  if (myrank == 0) { // Master
    printf("Nachrichtengröße [Byte]\tLatenz [ms]\tBandbreite [KByte/s]\n");
    for (i = 1; i <= MAX_ELEMENTS; i *= 2) {
      t = MPI_Wtime();
      for (j = 0; j < NUM_ITERATIONS; ++j) {
        MPI_Ssend(buf, i, MPI_BYTE, 1, 31337, MPI_COMM_WORLD);
        MPI_Recv(buf, i, MPI_BYTE, 1,    23, MPI_COMM_WORLD, &stat);
      }
      t = MPI_Wtime() - t;
      prefix(i, &msg_size, &mult);
      printf("%d%c\t%lf\t%14.1lf\n",
        msg_size, mult,
        t / (double) NUM_ITERATIONS * 1000 / 2,
        2 * NUM_ITERATIONS * sizeof(BYTE) * (double) i / t / 1024.0);
    }
  }
  else { // Slave
    for (i = 1; i <= MAX_ELEMENTS; i *= 2) {
      for (j = 0; j < NUM_ITERATIONS; ++j) {
        MPI_Recv(buf, i, MPI_BYTE, 0, 31337, MPI_COMM_WORLD, &stat);
        MPI_Ssend(buf, i, MPI_BYTE, 0,   23, MPI_COMM_WORLD);
      }
    }
  }

  // Aufräumen
  free(buf);
  MPI_Finalize();

  return 0;
}
