From 938c9c08d726485231bd86ba1389599732a61658 Mon Sep 17 00:00:00 2001 From: Joshua Bakita Date: Wed, 21 Jun 2023 11:36:12 -0400 Subject: Rewrite argument parsing for mon_cross_ctx_copies No longer requires a recompile to change the number or type of copy monitoring threads. --- copy_experiments/mon_cross_ctx_copies.cu | 93 ++++++++++++++++++++++---------- 1 file changed, 65 insertions(+), 28 deletions(-) (limited to 'copy_experiments/mon_cross_ctx_copies.cu') diff --git a/copy_experiments/mon_cross_ctx_copies.cu b/copy_experiments/mon_cross_ctx_copies.cu index b38d037..65ccd4e 100644 --- a/copy_experiments/mon_cross_ctx_copies.cu +++ b/copy_experiments/mon_cross_ctx_copies.cu @@ -13,6 +13,22 @@ #include #include #include +#include + +// Data for the help text(s) +const char* maintainer = ""; +const char* version = "mon_cross_ctx_copies 2023.06"; +const char* desc = + "Copy data to, from, or between GPU(s) and log the time that each block " + "of the copy completes. Data need not be transferred sequentially for " + "transfer times to be properly recorded."; + +const struct argp_option opts[] = { + {"copy_size", 's', "NUM_PAGES", 0, "Number of 4KiB pages to copy"}, + {"gpu", 'g', /*"DIRECTION"*/0, 0, "Add a GPU-monitored copy thread (can be duplicated)"}, + {"cpu", 'c', /*"DIRECTION"*/0, 0, "Add a CPU-monitored copy thread (can be duplicated)"}, + {0} +}; // CPU-only monitoring can only go a fraction of the speed of GPU monitoring. // This sets how much slower it should go. @@ -47,7 +63,7 @@ typedef enum { } my_config_t; typedef struct { - int *cond_var; + int is_ready; my_config_t config; } copy_thread_args_t; @@ -127,7 +143,7 @@ void* copy_thread(void* args_raw) { } // Tell our parent we're ready - *args->cond_var = 1; + args->is_ready = 1; // Wait for our parent to tell us to go while (!READY) continue; @@ -193,29 +209,42 @@ void* copy_thread(void* args_raw) { return times; } +const int MAX_THREADS = 32; + +typedef struct { + int num_threads; + copy_thread_args_t thread_args[MAX_THREADS]; +} global_args_t; + +static error_t arg_parser(int key, char* arg, struct argp_state *state) { + global_args_t* g_args = (global_args_t*)state->input; + switch (key) { + case 's': + if (atol(arg) < CPU_MON_DIVISOR * 2) + argp_error(state, "Please specify a larger copy size. It must be at least %d pages for accurate tracking.\n", CPU_MON_DIVISOR * 2); + COPY_SIZE = atol(arg) * PG_SZ; + break; + case 'g': + if (g_args->num_threads == MAX_THREADS) + argp_error(state, "Please specify less than %d monitoring threads, or increase the compiled in MAX_THREADS constraint.\n", MAX_THREADS); + g_args->thread_args[g_args->num_threads++] = {0, GPU_MONITORING}; + break; + case 'c': + if (g_args->num_threads == MAX_THREADS) + argp_error(state, "Please specify less than %d monitoring threads, or increase the compiled in MAX_THREADS constraint.\n", MAX_THREADS); + g_args->thread_args[g_args->num_threads++] = {0, CPU_MONITORING}; + break; + default: + return ARGP_ERR_UNKNOWN; + } + return 0; +} + int main(int argc, char**argv) { int tmp, dev; - const int NUM_THREADS = 4; - pthread_t t[NUM_THREADS]; - int is_thread_ready[NUM_THREADS] = {0}; - uint64_t *ctx_times[NUM_THREADS] = {0}; - // Setup thread condition variables and configuration (my_config_t enum) - copy_thread_args_t thread_args[NUM_THREADS] = { - {&is_thread_ready[0], CPU_MONITORING}, - {&is_thread_ready[1], CPU_MONITORING}, - {&is_thread_ready[2], CPU_MONITORING}, - {&is_thread_ready[3], CPU_MONITORING}, - };//, {&is_thread_ready[1], GPU_MONITORING}};//, {&is_thread_ready[2], CPU_MONITORING}, {&is_thread_ready[3], CPU_MONITORING}, {&is_thread_ready[4], CPU_MONITORING}, {&is_thread_ready[5], CPU_MONITORING}, {&is_thread_ready[6], CPU_MONITORING}, {&is_thread_ready[7], CPU_MONITORING}, {&is_thread_ready[8], CPU_MONITORING}}; - - if (argc != 2) { - fprintf(stderr, "Usage: %s <# of 4KB pages to copy to GPU>\n", argv[0]); - return 1; - } - if (atol(argv[1]) < CPU_MON_DIVISOR * 2) { - fprintf(stderr, "Please specify a larger copy size. It must be at least %d pages for accurate tracking.\n", CPU_MON_DIVISOR * 2); - return 2; - } - COPY_SIZE = atol(argv[1]) * PG_SZ; + uint64_t *ctx_times[MAX_THREADS] = {0}; + pthread_t t[MAX_THREADS]; + global_args_t g_args = {0}; // Due to some laziness in how we handle barriers, this flag needs to be true /// XXX: Still seems to work fine if it isn't??? @@ -226,6 +255,14 @@ int main(int argc, char**argv) { exit(1); }*/ + struct argp argp = {opts, arg_parser, 0, desc}; + argp_parse(&argp, argc, argv, 0, 0, &g_args); + + if (g_args.num_threads == 0) { + fprintf(stderr, "At least one copy thread must be specified with --gpu or --cpu arguments.\n"); + return 3; + } + // TODO: Rewrite this so that it's all (u)int64_t // Skip, as this is 1-to-1 on GV100 and GV11B //double d2h_scale = GetGPUTimerScale(dev); @@ -237,21 +274,21 @@ int main(int argc, char**argv) { srand(0); // Spawn and wait for children to intialize - for (int tid = 0; tid < NUM_THREADS; tid++) { - pthread_create(&t[tid], NULL, copy_thread, &thread_args[tid]); - while (!is_thread_ready[tid]) + for (int tid = 0; tid < g_args.num_threads; tid++) { + pthread_create(&t[tid], NULL, copy_thread, &g_args.thread_args[tid]); + while (!g_args.thread_args[tid].is_ready) continue; } // Tell children initialization is done and that they can go READY = 1; // Wait for threads to finish, and determine the earliest recorded time uint64_t smallest = UINT64_MAX; - for (int tid = 0; tid < NUM_THREADS; tid++) { + for (int tid = 0; tid < g_args.num_threads; tid++) { pthread_join(t[tid], (void**)&ctx_times[tid]); smallest = min(smallest, ctx_times[tid][0]); } // Rebase times to 0 and print - for (int tid = 0; tid < NUM_THREADS; tid++) { + for (int tid = 0; tid < g_args.num_threads; tid++) { // Summary to stderr fprintf(stderr, "%lu, %lu\n", ctx_times[tid][0] - smallest, ctx_times[tid][COPY_SIZE / PG_SZ - 1] - smallest); // Full dataset to stdout -- cgit v1.2.2