/* This program collects timing statistics for node-to-node message passing */

#include <stdio.h>
#include "pvm3.h"

#define NPROC	3
#define MAXSIZE	100000		/* must of a power of 10 */
#define ENCODE	PvmDataRaw	


#ifdef CM5_NODE

#include <cm/cmmd.h>
#define TIMER_CLEAR		CMMD_node_timer_clear(1) 
#define TIMER_START		CMMD_node_timer_start(1) 
#define TIMER_STOP		CMMD_node_timer_stop(1) 
#define TIMER_ELAPSED	CMMD_node_timer_elapsed(1) 

#endif /*CM5_NODE*/

#if defined(PGON_NODE) || defined(I860_NODE)

double tstart, tstop, dclock();
#define TIMER_CLEAR		(tstart = tstop = 0)
#define TIMER_START		(tstart = dclock())
#define TIMER_STOP		(tstop = dclock())
#define TIMER_ELAPSED	(tstop - tstart)

#endif /*PGON_NODE/I860_NODE*/


main()
{
	int mytid;				/* my task id */
	int tids[NPROC];		/* array of task IDs */

	mytid = pvm_mytid();
	tids[0] = pvm_parent();

	if (tids[0] < 0)
		mom(mytid, tids);
	else {
		pvm_recv(tids[0], 0);
		pvm_upkint(tids, NPROC, 1);
		printf("mytid = %d\n", mytid);
		if (mytid == tids[1])
			child1(tids);
		else
			child2(tids);
	}

	pvm_exit();
	exit();
}


mom(mytid, tids)
	int mytid;
	int tids[];
{
	int i;
	double data[MAXSIZE];
	char str[32];

	tids[0] = mytid;
	pvm_spawn("nntime", (char**)0, 0, "", NPROC-1, &tids[1]);
	pvm_initsend(ENCODE);
	pvm_pkint(tids, NPROC, 1);
	pvm_mcast(&tids[1], NPROC-1, 0);

	/* test host-node recv */

	pvm_recv(-1, 0);

	pvm_upkstr(str);
	printf("%s\n", str);
	pvm_upkdouble(data, MAXSIZE, 1);

	for (i = 0; i < MAXSIZE; i++)
		if ((data[i] - i*i) > 1.0) {
			printf("error: %g at i = %d\n", data[i], i);
			break;
		}

	if (i == MAXSIZE)
		printf("%d doubles received correctly\n", i);
}


child1(tids)
	int tids[];
{
	int size, n, i;
	double data[MAXSIZE];
	int t1, t2, t3;
	char str[32];

	/* test node-to-node recv */

	pvm_recv(-1, 0);
	pvm_upkstr(str);
	printf("%s\n", str);
	pvm_upkdouble(data, MAXSIZE, 1);

	for (i = 0; i < MAXSIZE; i++)
		if ((data[i] - i*i) > 1.0) {
			printf("error: %g at i = %g\n", data[i], i);
			break;
		}
	if (i == MAXSIZE)
		printf("%d doubles received correctly\n\n\n", i);

	
	/* do timing measurements */

	puts("Node-to-node Send/Ack\n");
	for (size = 1; size <= MAXSIZE; size *= 10) {

		for (n = 0; n < size; n++)
			data[n] = n*n;
		printf("Pack (us)   Send (us)   Ack (us)   (Data size = %d)", 8*size);
		puts("\n=======================================================");

		for (i = 0; i < 5; i++) {
#ifdef TIMER_CLEAR
			pvm_initsend(ENCODE);
			TIMER_CLEAR;
			TIMER_START;
			pvm_pkdouble(data, size, 1);
			TIMER_STOP;
			t1 = 1000000*TIMER_ELAPSED;
			TIMER_CLEAR;
			TIMER_START;
			pvm_send(tids[2], 0);
			TIMER_STOP;
			t2 = 1000000*TIMER_ELAPSED;
			TIMER_CLEAR;
			TIMER_START;
			pvm_recv(-1, 0);		/* receive ack */
			TIMER_STOP;
			t3 = 1000000*TIMER_ELAPSED;
			printf("%d           %d            %d\n", t1, t2, t3);
#endif
		}
		putchar('\n');
	}

	/* test node-to-host send */

	pvm_initsend(ENCODE);
	sprintf(str, "%d doubles from node one", MAXSIZE);
	pvm_pkstr("Hi from node one");
	pvm_pkdouble(data, MAXSIZE, 1);
	pvm_send(tids[0], 0);

	/* informs child2 to exit */
	pvm_initsend(ENCODE);
	pvm_pkstr("death");
	pvm_send(tids[2], 13);
}


child2(tids)
	int tids[];
{
	int n, dtid, bufid, type;
	double data[MAXSIZE];
	char str[32];

	/* test node-to-node send */

	for (n = 0; n < MAXSIZE; n++)
		data[n] = n*n;
	pvm_initsend(ENCODE);
	sprintf(str, "%d doubles from node two", MAXSIZE);
	pvm_pkstr(str);
	pvm_pkdouble(data, MAXSIZE, 1);
	pvm_send(tids[1], 0);

	pvm_initsend(ENCODE);
	pvm_pkint(&tids[1], 1, 1);

	/* receive data and ack */

	while(1) {
		bufid = pvm_recv(-1, -1);
		pvm_bufinfo(bufid, (int*)0, &type, &dtid);
		if (type == 13)
			break;
		pvm_send(dtid, 0);
	}
	pvm_exit();
	exit();
}
