def METHOD_NAME(*, name=None, func=None, args=None, cuda=False, out_file=None):
    # warmup
    futs = []
    for _ in range(NUM_RPC):
        futs.append(rpc.rpc_async("server", func, args=args))
    torch.futures.wait_all(futs)
    torch.cuda.current_stream(GPU_ID).synchronize()
    # warmup done
    timestamps = {}
    states = {
        "lock": threading.Lock(),
        "future": torch.futures.Future(),
        "pending": NUM_RPC,
    }
    def mark_complete(index, cuda, fut):
        timestamps[index]["tok"] = stamp_time(cuda)
        with states["lock"]:
            states["pending"] -= 1
            if states["pending"] == 0:
                states["future"].set_result(0)
    start = time.time()
    futs = []
    for index in range(NUM_RPC):
        timestamps[index] = {}
        timestamps[index]["tik"] = stamp_time(cuda)
        fut = rpc.rpc_async("server", func, args=args)
        futs.append(fut)
        fut.add_done_callback(partial(mark_complete, index, cuda))
    torch.futures.wait_all(futs)
    states["future"].wait()
    # torch.cuda.current_stream(GPU_ID).synchronize()
    delays = []
    for index in range(len(timestamps)):
        delays.append(compute_delay(timestamps[index], cuda))
    end = time.time()
    mean = sum(delays) / len(delays)
    stdv = stdev(delays)
    total = end - start
    print(
        f"{name}_{'cuda' if cuda else 'cpu'}: mean = {mean}, stdev = {stdv}, total = {end - start}",
        flush=True,
    )
    if out_file:
        out_file.write(f"{name}, {mean}, {stdv}, {total}\n")
    return mean, stdv, total