// ping_pong.cc
//
// Ascertain bandwidth in relation to packet size

#include <cstdlib>
#include <iostream>
#include <fstream>
#include "mpi.h"

using namespace std;

const int max_packet_size=0x400000;  // maximum size of a message
const int count=100;  // number of messages per measurement
char buff[max_packet_size];  // Send and receive buffer

int main(void) {

  MPI::Init();  // initialize MPI

  int rank=MPI::COMM_WORLD.Get_rank();  // ascertain own rank
  int size=MPI::COMM_WORLD.Get_size();  // and number of processes

  if (size==2) {  // exactly two processes needed for this process

    ofstream out;
    if (rank==0) {  // Open output file
      out.open("ping_pong.dat");
      if (!out)
        MPI::COMM_WORLD.Abort(EXIT_FAILURE);
      out << "# Data throughput relative to packet size" << endl
          << "# Time resolution " << MPI::Wtick() << " s" << endl
          << "# Packet size\tmean time\tmaximum time" << endl;
    }

    // Loop through various packet sizes
    int packet_size=1;
    while (packet_size<=max_packet_size) {
      double t_av=0.0; 
      double t_max=0.0; 
      // Loop through multiple messages
      for (int i=0; i<count; ++i) { 
        MPI::COMM_WORLD.Barrier();  // Synch processes 
        // Send and/or receive messages
        if (rank==0) { 
          MPI::COMM_WORLD.Barrier();  // Synch processes 
          double t=MPI::Wtime();      // Starting time
          MPI::COMM_WORLD.Send(buff, packet_size, MPI::CHAR, 1, 0); 
          MPI::COMM_WORLD.Barrier();  // Synch processes 
          t=MPI::Wtime()-t;           // Time vector
          t_av+=t; 
          if (t>t_max) 
            t_max=t;
        } else { 
          MPI::COMM_WORLD.Barrier();  // Synch processes
          MPI::COMM_WORLD.Recv(buff, packet_size, MPI::CHAR, 0, 0); 
          MPI::COMM_WORLD.Barrier();  // Synch processes 
        }
      } 
      if (rank==0) {  // Output results
        t_av/=count; 
        out << packet_size << "\t\t" << t_av << "\t" << t_max << endl; 
      }
      packet_size*=2;  // Double packet size
    }

    if (rank==0)  // Close output file
      out.close(); 
  }

  MPI::Finalize();  // Terminate MPI
   
  return EXIT_SUCCESS;
}