/*
 * bench_frame       : boilerplate for mpi program
 * measure(iter)     : measures `tf_dur` for $(iter) iterations
 * run_stat(N, var)  : run N measurements and obtain (avg, std) in sum1, sum2
 * warm_up(iter, dur): repeat until measurements (iter, dur) stabilize
 * report_latency(msgsize, MULTIPLICITY) : print a line of latency result
 */

subcode: bench_frame
    $include stdio
    $include stdlib
    $(if:HAS_MTEST)
        $include mpitest.h
    $(else)
        $include mpi

    $function main
        $(if:HAS_MTEST)
            MTest_Init(NULL, NULL);
        $(else)
            MPI_Init(NULL, NULL);

        $my grank, gsize: int
        MPI_Comm_rank(MPI_COMM_WORLD, &grank);
        MPI_Comm_size(MPI_COMM_WORLD, &gsize);
        $(if:MIN_PROCS)
            $if gsize < $(MIN_PROCS)
                printf("! Test $(_pagename) requires $(MIN_PROCS) processes !\n");
                return 1

        MPI_Comm comm = MPI_COMM_WORLD;

        $my void *buf
        $(if:HAS_MTEST)
            $call mtest_malloc, MAX_BUFSIZE
        $(else)
            buf = malloc(MAX_BUFSIZE)
        $if !buf
            printf("! Failed to allocate buffer (size=%d)\n", MAX_BUFSIZE)
            return 1

        $if grank == 0
            printf("TEST $(_pagename):\n")
            $call @report_header
        $call main
        $if grank == 0
            printf("\n")

        $(if:HAS_MTEST)
            MTest_Finalize(0);
        $(else)
            MPI_Finalize();

macros:
    use_double: 1

#----------------------------------------
subcode: _autoload
    $register_prefix(comm) MPI_Comm

subcode: foreach_size
    $for int size = 0; size < $(MAX_MSG); size = (size==0)?1:size*2
        $(set:MSG_SIZE=size)
        BLOCK

subcode: measure(iter)
    tf_start = MPI_Wtime()
    $for 0:$(iter)
        BLOCK
    tf_dur = MPI_Wtime() - tf_start

subcode: run_stat(N, var)
    $my double sum1=0, double sum2=0
    $for 0:$(N)
        BLOCK
        sum1 += $(var)
        sum2 += $(var) * $(var)
    sum1 /= $(N)
    sum2 /= $(N)
    sum2 = sqrt(sum2 - sum1 * sum1)

subcode: warm_up(iter, dur)
    $(set:MIN_ITER=(int) ($(iter) * 0.001 / $(dur)))
    $(iter) = 2
    $my double last_dur = 1.0
    $my int num_best = 0
    $while num_best < 10
        BLOCK
        $if $(iter) < $(MIN_ITER)
            $(iter) = $(MIN_ITER)
            num_best = 0
            continue
        # check that t_dur is no longer monotonically decreasing
        $if $(dur) > last_dur
            num_best++
        last_dur = $(dur)

subcode: header_latency
    printf("%12s %10s(us) %6s(us) %12s(MB/s)\n", "msgsize", "latency", "sigma", "bandwidth")

subcode: report_latency(MSGSIZE, MULTIPLICITY)
    $my tf_latency, tf_sigma, tf_bw
    tf_latency = sum1 / ($(MULTIPLICITY)) * 1e6
    tf_sigma = sum2 / ($(MULTIPLICITY)) * 1e6
    tf_bw = $(MSGSIZE) / tf_latency
    printf("%12d %10.3f     %6.3f     %12.3f\n", $(MSGSIZE), tf_latency, tf_sigma, tf_bw)

