/*
 * Defines following functions:
 *   bench_p2p
 *       bench_send, bench_warmup
 *       bench_recv
 *
 * For each measurement -
 *    First sender tells receiver the `iter` parameter. `iter = 0` means to quit.
 *    For each iteration runs `send_side` and `recv_side` assuming the measurement on sender side represents a latency measurement.
 * 
 * Caller page defines -
 *     subcode: sender_side, recv_side
 *     macro:
 *         MULTIPLICITY: divisor for each measurement
 */

macros:
    MEM_TYPES: sendrecv

subcode: _autoload
    $register_name(src) int
    $register_name(dst) int
    $register_name(buf) void *
    $register_name(size) int
    $define TAG 0
    $define SYNC_TAG 100

subcode: report_header
        $call header_latency

fncode: bench_p2p(comm, src, dst, buf, size)
    int rank;
    MPI_Comm_rank(comm, &rank)

    $(if:!MULTIPLICITY)
        $(set:MULTIPLICITY=1)

    $if rank == src
        iter = bench_warmup(comm, dst, buf, size)
        &call run_stat, NUM_REPEAT, tf_latency
            tf_latency = bench_send(iter, comm, dst, buf, size)
            tf_latency /= iter
        $call report_latency, size, $(MULTIPLICITY)
        $call send_stop
    $elif rank == dst
        bench_recv(comm, src, buf, size)

    subcode: send_stop
        iter = 0;
        MPI_Send(&iter, 1, MPI_INT, dst, SYNC_TAG, comm)

#---------------------------------------- 
fncode: bench_send(int iter, comm, dst, buf, size)
    # synchronize with receiver
    MPI_Send(&iter, 1, MPI_INT, dst, SYNC_TAG, comm);

    &call measure, iter
        $call @send_side

    return tf_dur

fncode: bench_recv(comm, src, buf, size)
    $while 1
        int iter;
        # synchronize with sender */
        MPI_Recv(&iter, 1, MPI_INT, src, SYNC_TAG, comm, MPI_STATUS_IGNORE);
        $if iter == 0
            # time to quit
            break
        $for i=0:iter
            $call @recv_side

fncode: bench_warmup(comm, dst, buf, size): int
    &call warm_up, iter, tf_dur
        tf_dur = bench_send(iter, comm, dst, buf, size)
    return iter
