diff --git a/.gitignore b/.gitignore index d5a6dd6b..8037a31a 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,17 @@ *.dSYM minimap2 mappy.c +data +.vscode/** +test.sam +*.sam +Log/** +debug/** +verf +trace +ncu +nsys +*_output* +workloads +.cmake/** +.depend \ No newline at end of file diff --git a/.gitmodules b/.gitmodules index a80f848d..7707bf77 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "lib/simde"] path = lib/simde url = https://github.com/nemequ/simde.git +[submodule "cJSON"] + path = cJSON + url = https://github.com/DaveGamble/cJSON.git diff --git a/LICENSE.txt b/LICENSE.txt index 1a06f649..e3089e3b 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -2,6 +2,7 @@ The MIT License Copyright (c) 2018- Dana-Farber Cancer Institute 2017-2018 Broad Institute, Inc. + 2022 Advanced Micro Devices, Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/Makefile b/Makefile index 4118616a..0f32d289 100644 --- a/Makefile +++ b/Makefile @@ -1,12 +1,16 @@ -CFLAGS= -g -Wall -O2 -Wc++-compat #-Wextra -CPPFLAGS= -DHAVE_KALLOC -INCLUDES= +CFLAGS= -O2 -g -DNDEBUG +CDEBUG_FLAGS= -g -O2 #-Wall -Wextra -Wno-unused-parameter -Wno-unused-variable -Wno-sign-compare -Wno-unused-function -Wno-c++17-extensions -Wno-\#warnings #-O0 -DNDEBUG +CPPFLAGS= -DHAVE_KALLOC -D__AMD_SPLIT_KERNELS__ # -Wno-unused-but-set-variable -Wno-unused-variable +CPPFLAGS+= $(if $(MAX_MICRO_BATCH),-DMAX_MICRO_BATCH=\($(MAX_MICRO_BATCH)\)) +INCLUDES= -I . OBJS= kthread.o kalloc.o misc.o bseq.o sketch.o sdust.o options.o index.o \ lchain.o align.o hit.o seed.o map.o format.o pe.o esterr.o splitidx.o \ ksw2_ll_sse.o -PROG= minimap2 +# PROG= minimap2-zerobranch-debug +# PROG= minimap2-nobalance-debug +PROG= minimap2$(SUFFIX) PROG_EXTRA= sdust minimap2-lite -LIBS= -lm -lz -lpthread +LIBS= -lm -lz -lpthread ifeq ($(arm_neon),) # if arm_neon is not defined ifeq ($(sse2only),) # if sse2only is not defined @@ -34,7 +38,21 @@ ifneq ($(tsan),) LIBS+=-fsanitize=thread endif -.PHONY:all extra clean depend + +# turn on debug flags +ifeq ($(DEBUG),info) + CFLAGS += -DDEBUG_PRINT +endif +ifeq ($(DEBUG), analyze) + CFLAGS += $(CDEBUG_FLAGS) + CFLAGS += -DDEBUG_CHECK -DDEBUG_PRINT +endif +ifeq ($(DEBUG), verbose) + CFLAGS += $(CDEBUG_FLAGS) + CFLAGS += -DDEBUG_CHECK -DDEBUG_PRINT -DDEBUG_VERBOSE +endif + +.PHONY:all extra clean depend # profile .SUFFIXES:.c .o .c.o: @@ -44,14 +62,25 @@ all:$(PROG) extra:all $(PROG_EXTRA) -minimap2:main.o libminimap2.a - $(CC) $(CFLAGS) main.o -o $@ -L. -lminimap2 $(LIBS) +# build cJSON +CJSON_OBJ= cJSON/cJSON.o +INCLUDES += -I cJSON +$(CJSON_OBJ): + make -C cJSON + +# build kernel objs +include gpu/gpu.mk + + +# compile with nvcc/hipcc +$(PROG):main.o libminimap2.a + $(GPU_CC) $(CFLAGS) $(GPU_FLAGS) main.o -o $@ -L. -lminimap2 $(LIBS) minimap2-lite:example.o libminimap2.a - $(CC) $(CFLAGS) $< -o $@ -L. -lminimap2 $(LIBS) + $(GPU_CC) $(CFLAGS) $(GPU_FLAGS) $< -o $@ -L. -lminimap2 $(LIBS) -libminimap2.a:$(OBJS) - $(AR) -csru $@ $(OBJS) +libminimap2.a:$(OBJS) $(CU_OBJS) $(CJSON_OBJ) + $(AR) -csru $@ $^ sdust:sdust.c kalloc.o kalloc.h kdq.h kvec.h kseq.h ketopt.h sdust.h $(CC) -D_SDUST_MAIN $(CFLAGS) $< kalloc.o -o $@ -lz @@ -97,7 +126,7 @@ ksw2_exts2_neon.o:ksw2_exts2_sse.c ksw2.h kalloc.h # other non-file targets -clean: +clean: cleangpu rm -fr gmon.out *.o a.out $(PROG) $(PROG_EXTRA) *~ *.a *.dSYM build dist mappy*.so mappy.c python/mappy.c mappy.egg* depend: @@ -129,4 +158,4 @@ pe.o: mmpriv.h minimap.h bseq.h kseq.h kvec.h kalloc.h ksort.h sdust.o: kalloc.h kdq.h kvec.h sdust.h seed.o: mmpriv.h minimap.h bseq.h kseq.h kalloc.h ksort.h sketch.o: kvec.h kalloc.h mmpriv.h minimap.h bseq.h kseq.h -splitidx.o: mmpriv.h minimap.h bseq.h kseq.h +splitidx.o: mmpriv.h minimap.h bseq.h kseq.h \ No newline at end of file diff --git a/a6000_config.json b/a6000_config.json new file mode 100644 index 00000000..0c946395 --- /dev/null +++ b/a6000_config.json @@ -0,0 +1,27 @@ +{ + "//config is for": "a6000. Fits one batch + 5% x 4 long buffer avg_read_n 10k", + "num_streams": 1, + "min_n": 512, + "//min_n": "queries with less anchors will be handled on cpu", + "long_seg_buffer_size": 258880000, + "max_total_n": 893440000, + "max_read": 893440, + "avg_read_n": 20000, + "//avg_read_n": "expect average number of anchors per read, not used if max_total_n and max_read are specified", + "range_kernel": { + "blockdim": 512, + "cut_check_anchors": 10, + "//cut_check_anchors": "Number of anchors to check to attemp a cut", + "anchor_per_block": 32768, + "//anchor_per_block": "Number of anchors each block handle. Must be int * blockdim" + }, + "score_kernel": { + "short_blockdim": 64, + "long_blockdim": 64, + "mid_blockdim": 64, + "//blockdim config": "options are not used: static config specified at compile time (make ... LONG_BLOCK_SIZE=1024)", + "short_griddim": 2688, + "long_griddim": 1024, + "mid_griddim": 2688 + } +} \ No newline at end of file diff --git a/aac_config.json b/aac_config.json new file mode 100644 index 00000000..c28dcdb4 --- /dev/null +++ b/aac_config.json @@ -0,0 +1,27 @@ +{ + "//config is for": "aac cloud. Fits one batch + 5% x 4 long buffer avg_read_n 10k", + "num_streams": 1, + "min_n": 512, + "//min_n": "queries with less anchors will be handled on cpu", + "long_seg_buffer_size": 1117376000, + "max_total_n": 2036880000, + "max_read": 2036880, + "avg_read_n": 20000, + "//avg_read_n": "expect average number of anchors per read, not used if max_total_n and max_read are specified", + "range_kernel": { + "blockdim": 512, + "cut_check_anchors": 10, + "//cut_check_anchors": "Number of anchors to check to attemp a cut", + "anchor_per_block": 32768, + "//anchor_per_block": "Number of anchors each block handle. Must be int * blockdim" + }, + "score_kernel": { + "short_blockdim": 64, + "long_blockdim": 64, + "mid_blockdim": 64, + "//blockdim config": "options are not used: static config specified at compile time (make ... LONG_BLOCK_SIZE=1024)", + "short_griddim": 16128, + "long_griddim": 208, + "mid_griddim": 16128 + } +} \ No newline at end of file diff --git a/cJSON b/cJSON new file mode 160000 index 00000000..b45f48e6 --- /dev/null +++ b/cJSON @@ -0,0 +1 @@ +Subproject commit b45f48e600671feade0b6bd65d1c69de7899f2be diff --git a/gfx1030.json b/gfx1030.json new file mode 100644 index 00000000..19c0c0aa --- /dev/null +++ b/gfx1030.json @@ -0,0 +1,28 @@ +{ + "//config is for": "AMD Radeon RX 6800 XT on amdxfx. Fits one batch + 5% x 4 long buffer avg_read_n 10k", + "num_streams": 1, + "min_n": 512, + "//min_n": "queries with less anchors will be handled on cpu", + "long_seg_buffer_size": 100000000, + "max_total_n": 493440000, + "max_read": 493440, + "avg_read_n": 20000, + "//avg_read_n": "expect average number of anchors per read, not used if max_total_n and max_read are specified", + "range_kernel": { + "blockdim": 512, + "cut_check_anchors": 10, + "//cut_check_anchors": "Number of anchors to check to attemp a cut", + "anchor_per_block": 32768, + "//anchor_per_block": "Number of anchors each block handle. Must be int * blockdim" + }, + "score_kernel": { + "micro_batch": 4, + "mid_blockdim": 512, + "//blockdim config": "options are not used: static config specified at compile time (make ... LONG_BLOCK_SIZE=1024)", + "short_griddim": 2688, + "long_griddim": 144, + "mid_griddim": 2688, + "long_seg_cutoff": 20, + "mid_seg_cutoff": 3 + } +} \ No newline at end of file diff --git a/gpu/debug.c b/gpu/debug.c new file mode 100644 index 00000000..3d4ce9ba --- /dev/null +++ b/gpu/debug.c @@ -0,0 +1,647 @@ +#include "debug.h" + +#include + +#include "plchain.h" + +// FILE *f_anchors = NULL; +// FILE *range_input = NULL; +// FILE *binary = NULL; +// bool use_binary_input = true; +// FILE *range = NULL; + +#ifdef DEBUG_VERBOSE + +///////////////////////////////////////////////////////////////////// +/////////// Print Input Files ///////////////////// +///////////////////////////////////////////////////////////////////// +void debug_output_anchors(const char debug_folder[], chain_read_t *in) { + static FILE *f_anchors = NULL; + if (!f_anchors) { + char anchors_filename[50]; + strcpy(anchors_filename, debug_folder); + strcat(anchors_filename, ".anchor.out"); + if ((f_anchors = fopen(anchors_filename, "w+")) == NULL) { + fprintf(stderr, "[Error] Cannot create output file %s\n", + anchors_filename); + exit(1); + } + fprintf(stderr, "[Info] Writing anchors to file %s\n", + anchors_filename); + fprintf(f_anchors, "@@@seq.name, in->seq.len); + fprintf(f_anchors, "*%d\n", in->rep_len); + + /* Read Number of Anchors */ + fprintf(f_anchors, "#%ld\n", in->n); + + /* Read Anchors */ + for (int i = 0; i < in->n; i++) { + fprintf(f_anchors, "%lx,%lx\t", in->a[i].x, in->a[i].y); + } + fprintf(f_anchors, "\n"); +} + +// DEBUG: not used +#if 0 +void debug_output_score(const char debug_folder[], chain_read_t *in) { + static FILE *f_score = NULL; + if (!f_score) { + char score_filename[50]; + strcpy(score_filename, debug_folder); + strcat(score_filename, ".goldscore.out"); + if ((f_score = fopen(score_filename, "w+")) == NULL) { + fprintf(stderr, "[Error] Cannot crea input file %s\n", + score_filename); + exit(1); + } + fprintf(stderr, "[Info] Writing gold score to file %s\n", + score_filename); + fprintf(f_score, "@@@seq.name, in->seq.len); + + /* Write Score and Predecesser */ + fprintf(f_score, "#%ld\n", in->n); + + /* Write score */ + for (int i = 0; i < in->n; i++) { + fprintf(f_score, "%d,%ld\t", in->f[i], in->p[i]); + } + fprintf(f_score, "\n"); +} +#endif + +void debug_output_meta(const char debug_folder[], input_meta_t *meta) { + static FILE *f_metaout = NULL; + if (!f_metaout) { + char *buf = NULL; + size_t len = 0; + char meta_filename[50]; + strcpy(meta_filename, debug_folder); + strcat(meta_filename, ".meta.out"); + if ((f_metaout = fopen(meta_filename, "w+")) == NULL) { + fprintf(stderr, "[Error] Cannot create input file %s\n", + meta_filename); + exit(1); + } + fprintf(stderr, "[Info] Writing meta data to file %s\n", meta_filename); + fprintf(f_metaout, "@@@>tname\ttlen\n"); + } + + /* Write Number of reference Sequences */ + fprintf(f_metaout, "#%d\n", meta->n_refs); + + /* Write Reference Seq metadata */ + for (int i = 0; i < meta->n_refs; i++) { + fprintf(f_metaout, ">%s\t%d\n", meta->refs[i].name, meta->refs[i].len); + } +} + +void debug_print_successor_range(int32_t *range, int64_t n) { + static FILE *fout_range = NULL; + static int read_idx = 0; + if (fout_range == NULL) { + char fout_range_filename[50]; + strcpy(fout_range_filename, debug_folder); + strcat(fout_range_filename, ".range.out"); + if ((fout_range = fopen(fout_range_filename, "w+")) == NULL) { + fprintf(stderr, "[Error]: Cannot create range output file: %s \n", + fout_range_filename); + exit(1); + } + fprintf(stderr, "[Info] Writing successor range to file %s\n", + fout_range_filename); + } + fprintf(fout_range, "> %d, len: %ld ", read_idx, n); + for (int64_t i = 0; i < n; ++i) { + fprintf(fout_range, "#%ld: %d, ", i, range[i]); + } + fprintf(fout_range, "\n"); + read_idx++; +} + +int debug_print_cut(const size_t *cut, size_t max_cut, size_t n, + size_t offset, char* qname) { + static FILE *fout_cut = NULL; + static int read_idx = 0; + if (fout_cut == NULL) { + char fout_cut_filename[50]; + strcpy(fout_cut_filename, debug_folder); + strcat(fout_cut_filename, ".cut.out"); + if ((fout_cut = fopen(fout_cut_filename, "w+")) == NULL) { + fprintf(stderr, "[Error]: Cannot create cut output file: %s \n", + fout_cut_filename); + exit(1); + } + fprintf(stderr, "[Info] Writing cut to file %s\n", fout_cut_filename); + } + fprintf(fout_cut, "> %s, len: %ld offset %ld ", qname == NULL ? "--" : qname, n, offset); + size_t cid = 0; + for (; cid < max_cut && (cut[cid] < n + offset || cut[cid] == SIZE_MAX); + cid++) { + if (cut[cid] != SIZE_MAX) + fprintf(fout_cut, "%zu(%zu)\t", cut[cid] - offset, cut[cid]); + else + fprintf(fout_cut, "x\t"); + } + fprintf(fout_cut, "\n"); + read_idx++; + return cid; +} + +void debug_print_score(const int64_t *p, const int32_t *score, int64_t n) { + static FILE *fout_score = NULL; + static int read_idx = 0; + if (fout_score == NULL) { + char fout_score_filename[50]; + strcpy(fout_score_filename, debug_folder); + strcat(fout_score_filename, ".score.out"); + if ((fout_score = fopen(fout_score_filename, "w+")) == NULL) { + fprintf(stderr, "[Error]: Cannot create score output file: %s \n", + fout_score_filename); + exit(1); + } + fprintf(stderr, "[Info] Writing score to file %s\n", + fout_score_filename); + fprintf(fout_score, "@@@> 32, (uint32_t)u[i]); + for (int new_j = j + (uint32_t)u[i]; j < new_j; j++) { + fprintf(fout_chain, "%lx,%lx ", a[j].x, a[j].y); + } + fprintf(fout_chain, "\n"); + } +} + +void debug_print_regs(mm_reg1_t* regs, int n_u, char* qname){ + static FILE *fout_regs = NULL; + if (fout_regs == NULL){ + char fout_regs_filename[50]; + strcpy(fout_regs_filename, debug_folder); + strcat(fout_regs_filename, ".regs.out"); + if ((fout_regs = fopen(fout_regs_filename, "w+")) == NULL) { + fprintf(stderr, "[Error]: Cannot create print output file: %s \n", + fout_regs_filename); + exit(1); + } + fprintf(stderr, "[Info] Writing regs to file %s\n", fout_regs_filename); + fprintf(fout_regs, "[regs] \n"); + } + fprintf(fout_regs, "<%s\n", qname); + for (int i = 0; i < n_u; i++){ + fprintf(fout_regs, + "[%d] cnt %d rid %d score %d qs %d qe %d rs %d re %d parent %d " + "subsc %d as %d mlen %d blen %d n_sub %d score0 %d\n", regs[i].id, + regs[i].cnt, regs[i].rid, regs[i].score, regs[i].qs, regs[i].qe, + regs[i].rs, regs[i].re, regs[i].parent, regs[i].subsc, + regs[i].as, regs[i].mlen, regs[i].blen, regs[i].n_sub, + regs[i].score0); + } +} + +FILE* fout_segs = NULL; +void debug_print_segs(seg_t* segs, chain_read_t* reads, int num_segs, int num_reads){ + if (fout_segs == NULL){ + char fout_segs_filename[50]; + strcpy(fout_segs_filename, debug_folder); + strcat(fout_segs_filename, ".long-segs.out"); + if ((fout_segs = fopen(fout_segs_filename, "w+")) == NULL) { + fprintf(stderr, "[Error]: Cannot create print output file: %s \n", + fout_segs_filename); + exit(1); + } + fprintf(stderr, "[Info] Writing segs to file %s\n", fout_segs_filename); + fprintf(fout_segs, "[segs] \n"); + } + fprintf(fout_segs, "Num Segs: %d, Num Reads: %d\n", num_segs, num_reads); + for (int i = 0; i < num_segs; i++){ + fprintf(fout_segs, "Seg #%d, %lu - %lu\n", i, segs[i].start_idx, segs[i].end_idx); + } + fflush(fout_segs); +} + +void debug_check_anchors(seg_t* segs, int num_segs, int32_t* ax_aggregated, int32_t* ax){ + size_t buffer_idx = 0; + for (int seg_id = 0; seg_id < num_segs; seg_id++) { + fprintf(fout_segs, "checking seg %lu - %lu...\n", segs[seg_id].start_idx, segs[seg_id].end_idx); + for (size_t i = segs[seg_id].start_idx; i < segs[seg_id].end_idx; i++) { + if (ax_aggregated[buffer_idx] != ax[i]) + fprintf(fout_segs, "Anchor mismatch: %d(%lu) %d(%lu)\n", ax_aggregated[buffer_idx], buffer_idx, ax[i], i); + buffer_idx++; + } + } +} + +#endif // DEBUG_VERBOSE + +/////////////////////////////////////////////////////////////////////////// +///////////// check functions /////////////////////////////// +/////////////////////////////////////////////////////////////////////////// +#ifdef DEBUG_CHECK + + +// DEBUG: uses with gold standard input score and range. SCORE CHECK +#if 0 +/** + * Read Plaintxt input file for Chaining scores from .score + * Allocate and Populate chain_read_t.f, chain_read_t.p + * Return number of anchors if success, -1 if failed + */ +int debug_skip_score_check = 0; +int debug_no_score_file = 0; +int debug_read_score(const char input_filename[], chain_read_t *in, void *km) { + static FILE *f_score = NULL; + if (debug_no_score_file) return -1; + if (!f_score) { + char *buf = NULL; + size_t len = 0; + char score_filename[50]; + strcpy(score_filename, input_filename); + strcat(score_filename, ".score"); + if ((f_score = fopen(score_filename, "r")) == NULL) { + in->f = NULL; + in->p = NULL; + debug_skip_score_check = 1; + debug_no_score_file = 1; + fprintf(stderr, "[Warning] Cannot open score file %s, skip score checking! \n", + score_filename); + return -1; + } + fprintf(stderr, "[Info] Reading gold score from file %s\n", + score_filename); + if (getline(&buf, &len, f_score) <= 0) { + // discard header line. + fprintf(stderr, "[Error] wrong format: %s %s\n", score_filename, buf); + return -1; + } + if (strlen(buf) < 3 || strncmp(buf, "@@@", 3)) { + fprintf(stderr, "[Error] wrong format %s %s\n", score_filename, buf); + return -1; + }; + kfree(km, buf); + } + + /* Read Sequence Name and Length */ + char buf[100]; + int seqlen = -1; + int num_anchor = -1; + if (fscanf(f_score, "<%s %d\n", buf, &seqlen) != 2) { + return -1; + } + + if (strcmp(buf, in->seq.name) || seqlen != in->seq.len) { + fprintf(stderr, "[Error] query sequence mismatch: %s %d\n", buf, + seqlen); + return -1; + } + + /* Read Score and Predecesser */ + if (fscanf(f_score, "#%ld\n", &num_anchor) != 1) return -1; + assert(num_anchor == in->n); + + /* Read Anchors */ + KMALLOC(km, in->p, in->n); + KMALLOC(km, in->f, in->n); + for (int i = 0; i < in->n; i++) { + if (fscanf(f_score, "%d,%ld", &in->f[i], &in->p[i]) != 2) return -1; + } + fscanf(f_score, "\n"); +#ifdef DEBUG_VERBOSE + debug_output_score(debug_folder, in); +#endif // DEBUG_VERBOSE + return in->n; +} + +#ifdef DEBUG_CHECK_FORCE + +/** + * Build ground truth score using backward_cpu + * Allocate and Populate chain_read_t.f, chain_read_t.p + * Return number of anchors + */ +int debug_build_score(chain_read_t *in, void *km) { + if (debug_skip_score_check){ + fprintf(stderr, + "[Info] force score checking against backward_cpu! \n"); + } + debug_skip_score_check = 0; + + /* Read Anchors */ + KMALLOC(km, in->p, in->n); + KMALLOC(km, in->f, in->n); + + Misc misc = build_misc(INT64_MAX); + mg_lchain_dp(misc.max_dist_x, misc.max_dist_y, misc.bw, misc.max_skip, + misc.max_iter, misc.min_cnt, misc.min_score, 0.12, + misc.chn_pen_skip, misc.is_cdna, misc.n_seg, in->n, in->a, + &in->n_u, &in->u, in->km, in, in->f, in->p); +#ifdef DEBUG_VERBOSE + debug_output_score(debug_folder, in); +#endif // DEBUG_VERBOSE + return in->n; +} + +#endif // DEBUG_CHECK_FORCE + +/** + * Check p[], f[] array against gold standard. + * Print if there is mismatch. +*/ +int debug_check_score(const int64_t *p, const int32_t *f, const int64_t *p_gold, + const int32_t *f_gold, int64_t n, char* qname) { + for (int64_t i = 0; i < n; ++i) { + if (f[i] == 0) { +#ifdef DEBUG_VERBOSE + fprintf(stderr, + "[Debug] Score Mismatch: %s Anchor %ld, score: %d (gold " + "x), previous: %ld (gold x)\n", + qname == 0 ? "--" : qname, i, f[i], p[i]); +#endif + } + } + if (debug_skip_score_check) return -1; + static int readid = 0; + size_t score_mismatches = 0; + int rt = 0; + for (int64_t i = 0; i < n; ++i) { + if (p[i] != p_gold[i] || f[i] != f_gold[i]) { +// #ifdef DEBUG_VERBOSE + fprintf(stderr, + "[Debug] Score Mismatch: %s Anchor %ld, score: %d (gold %d), " + "previous: %ld (gold %ld)\n", + qname == 0? "--": qname, readid, i, f[i], f_gold[i], p[i], p_gold[i]); +// #endif + rt = 1; + score_mismatches++; + } + } + if (rt == 1) + fprintf(stderr, "[Debug] Score Mismatch: %s %d mismatches\n", + qname == 0 ? "--" : qname, score_mismatches); + readid++; + return rt; +} +#endif // uses if we have gold standard input + + +void debug_check_range(const int32_t* range, size_t n){ + static int read_idx = 0; + for (size_t i = 1; i < n; i++){ + if (range[i] < range[i-1] - 1) + fprintf(stderr, "[debug]No realistic range sequence read #%d i %ld %d %d\n", read_idx, i, range[i-1], range[i]); + } + read_idx++; +} + +int debug_check_cut(const size_t *cut, const int32_t *range, size_t max_cut, + size_t n, size_t offset) { + + static int read_idx = 0; + size_t cid = 0; + for (; cid < max_cut && (cut[cid] < n + offset || cut[cid] == SIZE_MAX); + cid++) { + if (cut[cid] != SIZE_MAX) { + if (cut[cid] != 0 && range[cut[cid] - 1] != 0) + fprintf( + stderr, + "[debug] Cut Error: > %d Cut at %zu %lu (%d)\n", + read_idx, cut[cid], offset, range[cut[cid] - 1]); + } + if (cid > 0 && cut[cid] != SIZE_MAX){ + static size_t prev_cut = 0; + int cut_issue = 0; + for (size_t i = prev_cut; i < cut[cid]; i++) { + if (range[i] + i >= cut[cid]){ + fprintf(stderr, "[debug] Cut Error: > %d cid %ld , Cut %zu - %zu, i %zu, range %u\n", + read_idx, cid, prev_cut, cut[cid], i, range[i]); + cut_issue = 1; + } + } + if (cut_issue){ + for (int i = prev_cut; i < cut[cid]; i++){ + fprintf(stderr, "%u[%d]\t", i, range[i]); + } + fprintf(stderr, "\n"); + } + + prev_cut = cut[cid]; + } + } + read_idx++; + return cid; +} + + + + +// find long seg range distribution +void debug_cal_long_seg_range_dis(size_t total_n, size_t num_cut, int32_t* range){ +static uint64_t range_dis[5001] = {0}; + static size_t seg_total = 0; + static uint64_t anchors_total = 0; + static FILE* fp = NULL; + + for (size_t i = 0; i < total_n; i++){ + assert(range[i] <= 5000); + range_dis[range[i]]++; + } + anchors_total += total_n; + seg_total += num_cut; + if (!fp) { + fprintf(stderr, "[Debug] Writing to long_range_dis.csv\n"); + fp = fopen("long_range_dis.csv", "w+"); + fprintf(fp, "num_segs,num_anchors"); + for (int i = 0; i < 5001; i++) fprintf(fp, ",%d", i); + fprintf(fp, "\n"); + } + fprintf(fp, "%lusegs,%luanchors", seg_total, anchors_total); + for (int i = 0; i <= 5000; i++){ + fprintf(fp, ",%lu", range_dis[i]); + } + fprintf(fp, "\n"); +} + + +void debug_cal_mid_range_dis(size_t total_n, size_t num_cut, int32_t* range){ + static uint64_t range_dis[5001] = {0}; + static size_t seg_total = 0; + static uint64_t anchors_total = 0; + static FILE* fp = NULL; + + fprintf(stderr, "[verbose] %lu cuts generated\n", num_cut); + for (size_t i = 0; i < total_n; i++){ + assert(range[i] <= 5000); + range_dis[range[i]]++; + } + anchors_total += total_n; + seg_total += num_cut; + if (!fp) { + fprintf(stderr, "[Debug] Writing to mid_range_dis.csv\n"); + fp = fopen("mid_range_dis.csv", "w+"); + fprintf(fp, "num_segs,num_anchors"); + for (int i = 0; i < 5001; i++) fprintf(fp, ",%d", i); + fprintf(fp, "\n"); + } + fprintf(fp, "%lusegs,%luanchors", seg_total, anchors_total); + for (int i = 0; i < 5001; i++){ + fprintf(fp, ",%lu", range_dis[i]); + } + fprintf(fp, "\n"); +} + + +// range distribution +void debug_cal_range_dis(size_t total_n, size_t num_cut, int32_t* range){ + static uint64_t range_dis[5001] = {0}; + static size_t seg_total = 0; + static uint64_t anchors_total = 0; + static FILE* fp = NULL; + + fprintf(stderr, "[verbose] %lu cuts generated\n", num_cut); + for (size_t i = 0; i < total_n; i++){ + assert(range[i] <= 5000); + range_dis[range[i]]++; + } + anchors_total += total_n; + seg_total += num_cut; + if (!fp) { + fprintf(stderr, "[Debug] Writing to range_dis.csv\n"); + fp = fopen("range_dis.csv", "w+"); + fprintf(fp, "num_segs,num_anchors"); + for (int i = 0; i < 5001; i++) fprintf(fp, ",%d", i); + fprintf(fp, "\n"); + } + fprintf(fp, "%lusegs,%luanchors", seg_total, anchors_total); + for (int i = 0; i < 5001; i++){ + fprintf(fp, ",%lu", range_dis[i]); + } + fprintf(fp, "\n"); +} + +#define fine_grind 30 +// sc pair vs. seg length +void debug_cal_sc_pair_density(size_t total_n, size_t num_cut, size_t* cut, int32_t* range){ + // bin width: 10 cuts, max 5000 cuts + static uint64_t sc_pair_dis[(500+fine_grind)] = {0}; // number of sc pairs for each seg length + static uint64_t anchors_dis[(500+fine_grind)] = {0}; + static uint64_t seg_dis[(500+fine_grind)] = {0}; + + + uint64_t start_idx = 0, cut_size = 0; + for (int cid = 0; cid < num_cut; cid++) { + if (cut[cid] != SIZE_MAX) { + uint64_t sc_pair_num = 0; + for (uint64_t i = start_idx; i < cut[cid]; i++){ + sc_pair_num += range[i]; + } + if (cut_size < fine_grind){ + sc_pair_dis[cut_size] += sc_pair_num; + anchors_dis[cut_size] += cut[cid] - start_idx; + seg_dis[cut_size]++; + } else if (cut_size / 10 < 500) { + sc_pair_dis[cut_size/10 + fine_grind/9] += sc_pair_num; + anchors_dis[cut_size/10 + fine_grind/9] += cut[cid] - start_idx; + seg_dis[cut_size / 10 + fine_grind/9]++; + } else { + sc_pair_dis[500 + fine_grind/9] += sc_pair_num; + anchors_dis[500 + fine_grind/9] += cut[cid] - start_idx; + seg_dis[500 + fine_grind/9]++; + } + cut_size = 0; + start_idx = cut[cid]; + } else { + ++cut_size; + } + } + + static FILE* f_sc_pair_dis = NULL; + if (!f_sc_pair_dis){ + f_sc_pair_dis = fopen("sc_pair_dis.csv", "w+"); + fprintf(stderr, "[Verbose] writing to sc_pair_dis.csv"); + fprintf(f_sc_pair_dis, "seg_len"); + for(int i = 0; i < fine_grind; i++){ + fprintf(f_sc_pair_dis, ",%d", i); + } + for (int i = fine_grind/10; i <= 500; i++){ + fprintf(f_sc_pair_dis, ",%d", i*10); + } + fprintf(f_sc_pair_dis, "\n"); + } + + fprintf(f_sc_pair_dis, "sc_pairs"); + for (int i = 0; i < 500 + fine_grind; i++){ + fprintf(f_sc_pair_dis, ",%lu", sc_pair_dis[i]); + } + fprintf(f_sc_pair_dis, "\n"); + fprintf(f_sc_pair_dis, "anchors"); + for (int i = 0; i < 500 + fine_grind; i++){ + fprintf(f_sc_pair_dis, ",%lu", anchors_dis[i]); + } + fprintf(f_sc_pair_dis, "\n"); + fprintf(f_sc_pair_dis, "segs"); + for (int i = 0; i < 500 + fine_grind; i++){ + fprintf(f_sc_pair_dis, ",%lu", seg_dis[i]); + } + fprintf(f_sc_pair_dis, "\n"); + fflush(f_sc_pair_dis); +} + +#endif // DEBUG_CHECK \ No newline at end of file diff --git a/gpu/debug.h b/gpu/debug.h new file mode 100644 index 00000000..861e8ebd --- /dev/null +++ b/gpu/debug.h @@ -0,0 +1,82 @@ +#ifndef __DEBUG_H__ +#define __DEBUG_H__ +#include "plutils.h" +#include "mmpriv.h" + +#ifdef __cplusplus +extern "C" { +#endif + +const char debug_folder[] = "debug"; + +// #define ITER_LIMIT 10000 +// #define MAX_READ_NUM 100000 +// #define MEM_CPU (96-6) // 96 - 6 GB for possible exceed read +// #define MEM_GPU (16-4) // 16 - 4 GB as memory pool = 16760832(0xffc000) KB +// #define SATURATE_FACTOR (0.7) // NOTE: how much portion of cpu memory shall be allocated, < 1 + + +// /* Input File Names: Set by command line arguments in main.c */ +// extern char input_filename[]; // plaintxt chaining inputs & score +// extern char range_infile[]; // plaintxt range +// extern char binary_file[]; // binary chaining inputs & score +// extern char binary_range[]; // binary range + +#ifndef DEBUG_CHECK +#define ASSERT(X) +// Chaining Debug Checker: checks chaining score against input. +#elif DEBUG_CHECK +#define ASSERT(X) assert(X) +// Read score from file for comparison +int debug_read_score(const char input_filename[], chain_read_t *in, void *km); +int debug_build_score(chain_read_t *in, void *km); + +// Check score +int debug_check_score(const int64_t *p, const int32_t *f, const int64_t *p_gold, + const int32_t *f_gold, int64_t n, char* qname); +void debug_check_range(const int32_t* range, size_t n); +int debug_check_cut(const size_t *cut, const int32_t *range, size_t max_cut, + size_t n, size_t offset); + +#ifdef DEBUG_CHECK_FORCE +mm128_t *mg_lchain_dp(int max_dist_x, int max_dist_y, int bw, int max_skip, + int max_iter, int min_cnt, int min_sc, float chn_pen_gap, + float chn_pen_skip, int is_cdna, int n_seg, + int64_t n, // NOTE: n is number of anchors + mm128_t *a, // NOTE: a is ptr to anchors. + int *n_u_, uint64_t **_u, void *km, chain_read_t *input, + int32_t *f_, int64_t *p_ +); +#endif // DEBUG_CHECK_FORCE + + + +// Analyze Distribution + +void debug_cal_long_seg_range_dis(size_t total_n, size_t num_cut, int32_t* range); +void debug_cal_mid_range_dis(size_t total_n, size_t num_cut, int32_t *range); +void debug_cal_range_dis(size_t total_n, size_t num_cut, int32_t *range); +void debug_cal_sc_pair_density(size_t total_n, size_t num_cut, size_t* cut, int32_t* range); +#endif // DEBUG_CHECK + +#ifdef DEBUG_VERBOSE + // Print Input Files + void debug_output_anchors(const char debug_folder[], chain_read_t *in); +void debug_output_score(const char debug_folder[], chain_read_t *in); +void debug_output_meta(const char debug_folder[], input_meta_t *meta); + +void debug_print_successor_range(int32_t *range, int64_t n); +int debug_print_cut(const size_t *cut, size_t max_cut, size_t n, size_t offset, char* qname); +void debug_print_score(const int64_t *p, const int32_t *score, int64_t n); +void debug_print_score_rel_p(const uint16_t *p, const int32_t *score, int64_t n); +void debug_print_chain(mm128_t* a, uint64_t *u, int32_t n_u, char* qname); +void debug_print_regs(mm_reg1_t *regs, int n_u, char *qname); +void debug_print_segs(seg_t *segs, chain_read_t *reads, int num_segs, int num_reads); +void debug_check_anchors(seg_t* segs, int num_segs, int32_t* ax_aggregated, int32_t* ax); +#endif // DEBUG_VERBOSE + +#ifdef __cplusplus +} +#endif + +#endif// __DEBUG_H__ diff --git a/gpu/gpu.mk b/gpu/gpu.mk new file mode 100644 index 00000000..8b69c99e --- /dev/null +++ b/gpu/gpu.mk @@ -0,0 +1,62 @@ +GPU ?= AMD +CONFIG += $(if $(MAX_MICRO_BATCH),-DMICRO_BATCH=\($(MAX_MICRO_BATCH)\)) + +################################################### +############ CPU Compile ################### +################################################### +CU_SRC = $(wildcard gpu/*.cu) +CU_OBJS = $(CU_SRC:%.cu=%.o) +C_SRC = $(wildcard gpu/*.c) +OBJS += $(C_SRC:%.c=%.o) +INCLUDES += -I gpu + +################################################### +############ CUDA Compile ################### +################################################### +NVCC = nvcc +CUDAFLAGS = -rdc=true -lineinfo +CUDATESTFLAG = -G + +################################################### +############ HIP Compile ################### +################################################### +HIPCC = hipcc +HIPFLAGS = -DUSEHIP +HIPTESTFLAGS = -G -Rpass-analysis=kernel-resource-usage -ggdb +HIPLIBS = -L${ROCM_PATH}/lib -lroctx64 -lroctracer64 + +################################################### +############ DEBUG Options ################### +################################################### +ifeq ($(GPU), AMD) + GPU_CC = $(HIPCC) + GPU_FLAGS = $(HIPFLAGS) + GPU_TESTFL = $(HIPTESTFLAGS) + LIBS += $(HIPLIBS) +else + GPU_CC = $(NVCC) + GPU_FLAGS = $(CUDAFLAGS) + GPU_TESTFL = $(CUDATESTFLAG) +endif + +ifeq ($(DEBUG),analyze) + GPU_FLAGS += $(GPU_TESTFL) +endif + +%.o: %.cu + $(GPU_CC) -c $(GPU_FLAGS) $(CFLAGS) $(CPPFLAGS) $(INCLUDES) $(CONFIG) $< -o $@ + +cleangpu: + rm -f gpu/*.o + +# profile:CFLAGS += -pg -g3 +# profile:all +# perf record --call-graph=dwarf -e cycles:u time ./minimap2 -a test/MT-human.fa test/MT-orang.fa > test.sam + +cudep: gpu/.depend + +gpu/.depend: $(CU_SRC) + rm -f gpu/.depend + $(GPU_CC) -c $(GPU_FLAGS) $(CFLAGS) $(CPPFLAGS) $(INCLUDES) -MM $^ > $@ + +include gpu/.depend \ No newline at end of file diff --git a/gpu/hipify.cuh b/gpu/hipify.cuh new file mode 100644 index 00000000..90ee42c3 --- /dev/null +++ b/gpu/hipify.cuh @@ -0,0 +1,61 @@ +#ifndef __HIPIFY_CUH__ +#define __HIPIFY_CUH__ + +#ifdef USEHIP +#include "hip/hip_runtime.h" +#include "roctracer/roctx.h" +#define cudaDeviceProp hipDeviceProp_t +#define cudaGetDeviceProperties hipGetDeviceProperties +#define cudaMalloc hipMalloc +#define cudaMallocAsync hipMallocAsync +#define cudaMemcpy hipMemcpy +#define cudaMemcpyAsync hipMemcpyAsync +#define cudaMemcpyToSymbolAsync hipMemcpyToSymbolAsync +#define cudaMemcpyHostToDevice hipMemcpyHostToDevice +#define cudaMemcpyDeviceToHost hipMemcpyDeviceToHost +#define cudaDeviceSynchronize hipDeviceSynchronize +#define cudaFree hipFree +#define cudaFreeAsync hipFreeAsync +#define cudaMemcpyToSymbol hipMemcpyToSymbol +#define cudaMemset hipMemset +#define cudaMemsetAsync hipMemsetAsync +#define cudaStream_t hipStream_t +#define cudaStreamCreate hipStreamCreate +#define cudaStreamSynchronize hipStreamSynchronize +#define cudaStreamDestroy hipStreamDestroy +#define cudaMallocHost hipHostMalloc +#define cudaFreeHost hipHostFree +#define cudaEvent_t hipEvent_t +#define cudaEventCreate hipEventCreate +#define cudaEventRecord hipEventRecord +#define cudaEventQuery hipEventQuery +#define cudaEventDestroy hipEventDestroy +#define cudaEventElapsedTime hipEventElapsedTime +#define cudaStreamWaitEvent hipStreamWaitEvent +#define cudaMemGetInfo hipMemGetInfo +#define cudaCheck() { \ + hipError_t err = hipGetLastError(); \ + if (hipSuccess != err) { \ + fprintf(stderr, "Error in %s:%i %s(): %s.\n", __FILE__, __LINE__, \ + __func__, hipGetErrorString(err)); \ + fflush(stderr); \ + exit(EXIT_FAILURE); \ + } \ +} +#define cudaWarpSize 64 +#else +#define cudaCheck() { \ + cudaError_t err = cudaGetLastError(); \ + if (cudaSuccess != err) { \ + fprintf(stderr, "Error in %s:%i %s(): %s.\n", __FILE__, __LINE__,\ + __func__, cudaGetErrorString(err)); \ + fflush(stderr); \ + exit(EXIT_FAILURE); \ + } \ +} +#include + +#endif + + +#endif // __HIPIFY_CUH__ diff --git a/gpu/planalyze.cu b/gpu/planalyze.cu new file mode 100644 index 00000000..cfa8a5c9 --- /dev/null +++ b/gpu/planalyze.cu @@ -0,0 +1,201 @@ +/* Implement kernel performance analysis that requires extra device + * synchornization. disabled unless DEBUG_LEVEL is set to analyze. + * Enable individual verbose prints in planalyze.cu + */ +#include "planalyze.cuh" + +#ifdef DEBUG_CHECK +void planalyze_short_kernel(stream_ptr_t stream, int uid, float throughput[]){ + cudaStreamSynchronize(stream.cudastream); + size_t total_n = stream.host_mems[uid].total_n; + chain_read_t* reads = stream.reads; + deviceMemPtr* dev_mem = &stream.dev_mem; + hostMemPtr* host_mem = &stream.host_mems[uid]; + size_t cut_num = stream.host_mems[uid].cut_num; + unsigned int num_mid_seg, num_long_seg; + cudaMemcpy(&num_mid_seg, dev_mem->d_mid_seg_count, sizeof(unsigned int), + cudaMemcpyDeviceToHost); + num_long_seg = host_mem->long_segs_num[0] - (uid>0 ? stream.host_mems[uid-1].long_segs_num[0] : 0); + cudaMemcpy(&num_long_seg, dev_mem->d_long_seg_count, sizeof(unsigned int), + cudaMemcpyDeviceToHost); +#ifdef DEBUG_VERBOSE + fprintf(stderr, "[DEBUG](MICROBATCH# %d) total segs: %lu, short:%lu mid: %u \n", uid, cut_num, cut_num - num_mid_seg - num_long_seg, num_mid_seg); +#endif // DEBUG_VERBOSE + + int32_t* range = (int32_t*)malloc(sizeof(int32_t) * total_n); + cudaMemcpy(range, dev_mem->d_range, sizeof(int32_t) * total_n, + cudaMemcpyDeviceToHost); + size_t* cut = (size_t*)malloc(sizeof(size_t) * cut_num); + cudaMemcpy(cut, dev_mem->d_cut, sizeof(size_t) * cut_num, + cudaMemcpyDeviceToHost); + + seg_t* mid_segs = (seg_t*)malloc(sizeof(seg_t) * num_mid_seg); + cudaMemcpy(mid_segs, dev_mem->d_mid_seg, sizeof(seg_t) * num_mid_seg, + cudaMemcpyDeviceToHost); + + + longMemPtr* long_mem = &stream.long_mem; + unsigned int num_aggregated_long_segs; + cudaMemcpy(&num_aggregated_long_segs, dev_mem->d_long_seg_count, sizeof(unsigned int), + cudaMemcpyDeviceToHost); +#ifdef DEBUG_VERBOSE + fprintf(stderr, + "[DEBUG] aggreagated num of long segs %u, %u-%u belongs to this " + "minibatch\n", + num_aggregated_long_segs, + uid > 0 ? stream.host_mems[uid-1].long_segs_num[0] : 0, + num_aggregated_long_segs); +#endif // DEBUG_VERBOSE + + seg_t* long_segs = (seg_t*)malloc(sizeof(seg_t) * num_aggregated_long_segs); + cudaMemcpy(long_segs, dev_mem->d_long_seg, sizeof(seg_t) * num_aggregated_long_segs, + cudaMemcpyDeviceToHost); + + size_t long_segs_total_n; + cudaMemcpy(&long_segs_total_n, dev_mem->d_total_n_long, sizeof(size_t), cudaMemcpyDeviceToHost); + int32_t* long_range = (int32_t*)malloc(sizeof(int32_t) * long_segs_total_n); + cudaMemcpy(long_range, dev_mem->d_range_long, sizeof(int32_t) * long_segs_total_n, cudaMemcpyDeviceToHost); + +// Calculate long segs total workload (sc pairs) + size_t long_seg_sc_pairs = 0; + for(unsigned int segid = (uid>0 ? stream.host_mems[uid-1].long_segs_num[0] : 0); segid < num_aggregated_long_segs; segid++){ + for (size_t i = long_segs[segid].start_idx; i < long_segs[segid].end_idx; i++) + long_seg_sc_pairs += long_range[i]; + } +#ifdef DEBUG_VERBOSE + fprintf(stderr, "[DEBUG] workload (sc pairs) in long segs: %lu\n", long_seg_sc_pairs); +#endif // DEBUG_VERBOSE + +// Calculate total workload (sc pairs) + size_t total_sc_pairs = 0; + for (size_t i = 0; i < total_n; i++) { + total_sc_pairs += range[i]; + } + +#ifdef DEBUG_VERBOSE + fprintf(stderr, "[DEBUG] Total workload (sc pairs) in batch: %lu. %.2f%% work are in long segs.\n", total_sc_pairs, (float)long_seg_sc_pairs/total_sc_pairs*100); +#endif // DEBUG_VERBOSE + assert(long_seg_sc_pairs <= total_sc_pairs); + + // calculate short kernel throughput + float short_kernel_runtime_ms = 0; + cudaEventElapsedTime(&short_kernel_runtime_ms, stream.short_kernel_start_event[uid], stream.short_kernel_stop_event[uid]); + throughput[uid] = (total_sc_pairs - long_seg_sc_pairs) / short_kernel_runtime_ms / (float)1000; +#ifdef DEBUG_VERBOSE + fprintf(stderr, "[DEBUG] Short Seg kernel #%d throughput: %.2f Mpairs/s\n", uid, throughput[uid]); +#endif // DEBUG_VERBOSE + +// Check range w.r.t input (MAKE SURE INPUT RANGE EXISTS) +#if 0 + int64_t read_start = 0; + for (int i = 0; i < dev_mem->size; i++) { +// DEBUG: print range +#if defined(DEBUG_VERBOSE) && 0 + debug_print_successor_range(range + read_start, reads[i].n); +#endif + debug_check_range(range + read_start, input_arr[i].range, input_arr[i].n); + read_start += reads[i].n; + } +#endif + +// DEBUG: Check voilation of cut +#if defined(DEBUG_CHECK) && 0 + for (int readid = 0, cid = 0, idx = 0; readid < dev_mem->size; readid++) { +// DEBUG: Print cuts +#if defined(DEBUG_VERBOSE) && 0 + debug_print_cut(cut + cid, cut_num - cid, reads[readid].n, idx, reads[readid].seq.name); +#endif + cid += debug_check_cut(cut + cid, range, cut_num - cid, reads[readid].n, idx); + idx += reads[readid].n; + } +#endif + +#if defined(DEBUG_VERBOSE) && 0 + int32_t* ax = (int32_t*) malloc(sizeof(int32_t) * dev_mem->buffer_size_long); + cudaMemcpy(ax, dev_mem->d_ax_long, sizeof(int32_t) * dev_mem->buffer_size_long, cudaMemcpyDeviceToHost); + debug_print_segs(host_mem->long_segs, reads, host_mem->long_segs_num[0], stream.host_mems[uid].size); + debug_check_anchors(host_mem->long_segs, host_mem->long_segs_num[0], ax, host_mem->ax); +#endif + +//DEBUG: Calculate range distribution +#if defined(DEBUG_VERBOSE) && 0 + debug_cal_range_dis(total_n, cut_num, range); +#endif // DEBUG_VERBOSE + +// Calculate range distribution for mid segs +#if defined(DEBUG_VERBOSE) && 0 + for (int seg_id = 0; seg_id < num_mid_seg; seg_id++){ + debug_cal_mid_range_dis(mid_segs[seg_id].end_idx - mid_segs[seg_id].start_idx, 1, range + mid_segs[seg_id].start_idx); + } +#endif // DEBUG_VERBOSE + +// DEBUG: Calculate workload distribution +#if defined(DEBUG_VERBOSE) && 0 + debug_cal_sc_pair_density(total_n, cut_num, cut, range); +#endif // DEBUG_VERBOSE + + free(cut); + free(range); + +} +#endif + + + + +#ifdef DEBUG_CHECK + +void planalyze_long_kernel(stream_ptr_t stream, float* throughput){ + deviceMemPtr* dev_mem = &stream.dev_mem; + longMemPtr* long_mem = &stream.long_mem; + + unsigned int num_long_seg = long_mem->total_long_segs_num[0]; +#ifdef DEBUG_VERBOSE + fprintf(stderr, "[DEBUG]LONG Kernel: num of long segs %u\n", num_long_seg); +#endif // DEBUG_VERBOSE + + + seg_t* long_segs = (seg_t*)malloc(sizeof(seg_t) * num_long_seg); + cudaMemcpy(long_segs, dev_mem->d_long_seg, sizeof(seg_t) * num_long_seg, + cudaMemcpyDeviceToHost); + int32_t* long_range = (int32_t*)malloc(sizeof(int32_t) * *(long_mem->total_long_segs_n)); + cudaMemcpy(long_range, dev_mem->d_range_long, sizeof(int32_t) * *(long_mem->total_long_segs_n), cudaMemcpyDeviceToHost); +#ifdef DEBUG_VERBOSE + fprintf(stderr, "[DEBUG] Total n of anchors in long segs %lu\n", *long_mem->total_long_segs_n); +#endif // DEBUG_VERBOSE + +// Calculate total workload (sc pairs) + size_t long_seg_sc_pairs = 0; + for(size_t i = 0; i < *long_mem->total_long_segs_n; i++){ + long_seg_sc_pairs += long_range[i]; + } +#ifdef DEBUG_VERBOSE + fprintf(stderr, "[DEBUG] workload (sc pairs) in long kernel: %lu\n", long_seg_sc_pairs); +#endif // DEBUG_VERBOSE + + + // calculate long kernel throughput + float long_kernel_runtime_ms = 0; + cudaEventElapsedTime(&long_kernel_runtime_ms, stream.long_kernel_event, stream.stopevent); + float long_kernel_througput = long_seg_sc_pairs / long_kernel_runtime_ms / (float)1000; +#ifdef DEBUG_VERBOSE + fprintf(stderr, "[DEBUG] Long Seg kernel throughput: %.2f Mpairs/s\n", long_kernel_througput); +#endif // DEBUG_VERBOSE + throughput[score_kernel_config.micro_batch] = long_kernel_througput; + +// Check range w.r.t input (MAKE SURE INPUT RANGE EXISTS) +#if defined(DEBUG_VERBOSE) && 0 + debug_print_successor_range(long_range, *long_mem->total_long_segs_n); +#endif + +//DEBUG: Calculate range distribution +#if defined(DEBUG_VERBOSE) && 0 + debug_cal_long_seg_range_dis(*long_mem->total_long_segs_n, num_long_seg, long_range); +#endif // DEBUG_VERBOSE + + free(long_segs); + free(long_range); + +} + +#endif // DEBUG_CHECK \ No newline at end of file diff --git a/gpu/planalyze.cuh b/gpu/planalyze.cuh new file mode 100644 index 00000000..cef1048e --- /dev/null +++ b/gpu/planalyze.cuh @@ -0,0 +1,21 @@ +#ifndef __PLANALYZE_H__ +#define __PLANALYZE_H__ + +/* Implement kernel performance analysis that requires extra device + * synchornization. disabled unless DEBUG_LEVEL is set to analyze. + * Enable individual verbose prints in planalyze.cu + */ + +#include "hipify.cuh" +#include "plchain.h" +#include "plutils.h" +#include "plmem.cuh" +#include "plscore.cuh" + + +#ifdef DEBUG_CHECK +void planalyze_short_kernel(stream_ptr_t stream, int uid, float throughput[]); +void planalyze_long_kernel(stream_ptr_t stream, float* throughput); +#endif // DEBUG_CHECK + +#endif // __PLANALYZE_H__ \ No newline at end of file diff --git a/gpu/plchain.cu b/gpu/plchain.cu new file mode 100644 index 00000000..c0418d20 --- /dev/null +++ b/gpu/plchain.cu @@ -0,0 +1,560 @@ +#include +#include +#include +#include +#include + + +#include "mmpriv.h" +#include "plmem.cuh" +#include "plrange.cuh" +#include "plscore.cuh" +#include "plchain.h" +#include +#include + +#ifdef DEBUG_CHECK +#include "planalyze.cuh" +#include "debug.h" +#endif // DEBUG_CHECK + +// utils functions +struct +{ + bool operator()(std::pair a, std::pair b) const { + return a.first > b.first; + } +} +comp; + +void pairsort(seg_t *sdata, unsigned *map, unsigned N){ + std::pair elements[N]; + for (unsigned i = 0; i < N; ++i){ + elements[i].second = map[i]; + elements[i].first = sdata[i].end_idx - sdata[i].start_idx; + } + + std::sort(elements, elements+N, comp); // descent order + + for (unsigned i = 0; i < N; ++i){ + map[i] = elements[i].second; + } +} + + +/** + * translate relative predecessor index to abs index + * Input + * rel[] relative predecessor index + * Output + * p[] absolute predecessor index (of each read) + */ +void p_rel2idx(const uint16_t* rel, int64_t* p, size_t n) { + for (int i = 0; i < n; ++i) { + if (rel[i] == 0) + p[i] = -1; + else + p[i] = i - rel[i]; + } +} + +////////////////////////////////////////////////////////////////////////// +/////////// Backtracking ////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// + +/** + * @brief start from end index of the chain, find the location of min score on + * the chain until anchor has no predecessor OR anchor is in another chain + * + * @param max_drop + * @param z [in] {sc, anchor idx}, sorted by sc + * @param f [in] score + * @param p [in] predecessor + * @param k [in] chain end index + * @param t [update] 0 for unchained anchor, 1 for chained anchor + * @return min_i minmute score location in the chain + */ + +static int64_t mg_chain_bk_end(int32_t max_drop, const mm128_t *z, + const int32_t *f, const int64_t *p, int32_t *t, + int64_t k) { + int64_t i = z[k].y, end_i = -1, max_i = i; + int32_t max_s = 0; + if (i < 0 || t[i] != 0) return i; + do { + int32_t s; + t[i] = 2; + end_i = i = p[i]; + s = i < 0 ? z[k].x : (int32_t)z[k].x - f[i]; + if (s > max_s) + max_s = s, max_i = i; + else if (max_s - s > max_drop) + break; + } while (i >= 0 && t[i] == 0); + for (i = z[k].y; i >= 0 && i != end_i; i = p[i]) // reset modified t[] + t[i] = 0; + return max_i; +} + +void plchain_backtracking(hostMemPtr *host_mem, chain_read_t *reads, Misc misc, void* km){ + int max_drop = misc.bw; + if (misc.max_dist_x < misc.bw) misc.max_dist_x = misc.bw; + if (misc.max_dist_y < misc.bw && !misc.is_cdna) misc.max_dist_y = misc.bw; + if (misc.is_cdna) max_drop = INT32_MAX; + + size_t n_read = host_mem->size; + + uint16_t* p_hostmem = host_mem->p; + int32_t* f = host_mem->f; + for (int i = 0; i < n_read; i++) { + int64_t* p; + KMALLOC(km, p, reads[i].n); + p_rel2idx(p_hostmem, p, reads[i].n); +// print scores +#if defined(DEBUG_VERBOSE) && 0 + debug_print_score(p, f, reads[i].n); +#endif +// Check score w.r.t to input (MAKE SURE INPUT SCORE EXISTS: search for SCORE CHECK) +#if defined(DEBUG_CHECK) && 0 + debug_check_score(p, f, reads[i].p, reads[i].f, reads[i].n); +#endif + + /* Backtracking */ + uint64_t* u; + int32_t *v, *t; + KMALLOC(km, v, reads[i].n); + KCALLOC(km, t, reads[i].n); + int32_t n_u, n_v; + u = mg_chain_backtrack(km, reads[i].n, f, p, v, t, misc.min_cnt, misc.min_score, max_drop, &n_u, &n_v); + reads[i].u = u; + reads[i].n_u = n_u; + kfree(km, p); + // here f is not managed by km memory pool + kfree(km, t); + if (n_u == 0) { + kfree(km, reads[i].a); + kfree(km, v); + reads[i].a = 0; + + f += reads[i].n; + p_hostmem += reads[i].n; + continue; + } + + mm128_t* new_a = compact_a(km, n_u, u, n_v, v, reads[i].a); + reads[i].a = new_a; + + f += reads[i].n; + p_hostmem += reads[i].n; + } +} + + +////////////////////////////////////////////////////////////////////////// +/////////// Stream Management ///////////////////////////////// +////////////////////////////////////////////////////////////////////////// + +/** + * Wait and find a free stream + * stream_setup: [in] + * batchid: [in] + * stream_id: [out] stream_id to schedule to + * RETURN + * true if need to cleanup current stream +*/ +int plchain_schedule_stream(const streamSetup_t stream_setup, const int batchid){ + /* Haven't fill all the streams*/ + if (batchid < stream_setup.num_stream) { + return batchid; + } + + // wait until one stream is free + int streamid = -1; + while(streamid == -1){ + for (int t = 0; t < stream_setup.num_stream; t++){ + if (!cudaEventQuery(stream_setup.streams[t].stopevent)) { + streamid = t; + // FIXME: unnecessary recreate? + cudaEventDestroy(stream_setup.streams[t].stopevent); + cudaEventCreate(&stream_setup.streams[t].stopevent); + cudaCheck(); + break; + } + // cudaCheck(); + } + } + return streamid; +} + + +// Global variable for debug prints. Throughput, runtime & mem usage +#ifdef DEBUG_PRINT + float kernel_mem_usage[MAX_MICRO_BATCH + 1] = {0}; + float kernel_throughput[MAX_MICRO_BATCH + 1] = {0}; +#endif // DEBUG_PRINT + +/* + * Accepts a stream that has already been synced, and finished processing a batch + * Finish and cleanup the stream, save primary chain results to unpinned CPU memory. + * RETURN: number of reads in last batch +*/ +int plchain_post_gpu_helper(streamSetup_t stream_setup, int stream_id, Misc misc, void* km){ + int n_reads = 0; // Number of reads in the batch + +#if defined(DEBUG_CHECK) + planalyze_long_kernel(stream_setup.streams[stream_id], kernel_throughput); +#endif // DEBUG_CHECK + +#ifdef DEBUG_PRINT + kernel_mem_usage[score_kernel_config.micro_batch] = (float)(*stream_setup.streams[stream_id].long_mem.total_long_segs_n)/stream_setup.long_seg_buffer_size_stream*100; + + float kernel_runtime_ms[MAX_MICRO_BATCH + 1] = {0}; + float kernel_throughput_anchors[MAX_MICRO_BATCH + 1] = {0}; + cudaEventElapsedTime(&kernel_runtime_ms[score_kernel_config.micro_batch], stream_setup.streams[stream_id].long_kernel_event, stream_setup.streams[stream_id].stopevent); + kernel_throughput_anchors[score_kernel_config.micro_batch] = *stream_setup.streams[stream_id].long_mem.total_long_segs_n / kernel_runtime_ms[score_kernel_config.micro_batch] / (float)1000; +#endif + + + seg_t* long_segs = stream_setup.streams[stream_id].long_mem.long_segs_og_idx; + size_t long_seg_idx = 0; + size_t long_i = 0; + for (int uid = 0; uid < score_kernel_config.micro_batch; uid++) { + if (stream_setup.streams[stream_id].host_mems[uid].size == 0) continue; + // regorg long to each host mem ptr + // NOTE: this is the number of long segs till this microbatch + unsigned int long_segs_num = stream_setup.streams[stream_id].host_mems[uid].long_segs_num[0]; +#ifdef DEBUG_VERBOSE + fprintf(stderr, "[Debug] %s (%s:%d) MICROBATCH$%d FINISHED: long seg %lu - %u", + __func__, __FILE__, __LINE__, uid, long_seg_idx, long_segs_num); +#endif // DEBUG_VERBOSE + size_t total_n_long_segs = 0; + for (; long_seg_idx < long_segs_num; long_seg_idx++) { + for (size_t i = long_segs[long_seg_idx].start_idx; + i < long_segs[long_seg_idx].end_idx; i++, long_i++) { + stream_setup.streams[stream_id].host_mems[uid].f[i] = + stream_setup.streams[stream_id].long_mem.f_long[long_i]; + stream_setup.streams[stream_id].host_mems[uid].p[i] = + stream_setup.streams[stream_id].long_mem.p_long[long_i]; + } + total_n_long_segs += long_segs[long_seg_idx].end_idx - long_segs[long_seg_idx].start_idx; + } + + // backtrack after p/f is copied + plchain_backtracking(&stream_setup.streams[stream_id].host_mems[uid], + stream_setup.streams[stream_id].reads + n_reads, misc, km); + // accumulate n_reads + n_reads += stream_setup.streams[stream_id].host_mems[uid].size; +#ifdef DEBUG_PRINT + cudaEventElapsedTime(&kernel_runtime_ms[uid], stream_setup.streams[stream_id].short_kernel_start_event[uid], stream_setup.streams[stream_id].short_kernel_stop_event[uid]); + kernel_throughput_anchors[uid] = + (stream_setup.streams[stream_id].host_mems[uid].total_n - total_n_long_segs) / + kernel_runtime_ms[uid] / (float)1000; +#ifdef DEBUG_VERBOSE + fprintf(stderr, ", %.2f%% anchors are in long segs. \n", (float)total_n_long_segs / stream_setup.streams[stream_id].host_mems[uid].total_n * 100); +#endif // DEBUG_VERBOSE +#endif // DEBUG_PRINT + } + +#ifdef DEBUG_PRINT + fprintf(stderr, "----------------------------------------------------------------------------\n "); + for (int uid = 0; uid < score_kernel_config.micro_batch; uid++) fprintf(stderr, "Short%d ", uid); + fprintf(stderr, "Long\n"); + fprintf(stderr, "----------------------------------------------------------------------------\n"); + fprintf(stderr, "Mem Usage = "); + for (int uid = 0; uid < score_kernel_config.micro_batch; uid++) fprintf(stderr, " %9.2f%%", kernel_mem_usage[uid]); + fprintf(stderr, " %9.2f %%\n", kernel_mem_usage[score_kernel_config.micro_batch]); + fprintf(stderr, "Runtime(s) = "); + for (int uid = 0; uid < score_kernel_config.micro_batch; uid++) fprintf(stderr, "%11.2f", kernel_runtime_ms[uid] / 1000); + fprintf(stderr, " %11.2f\n", kernel_runtime_ms[score_kernel_config.micro_batch] / 1000); + fprintf(stderr, "BW (Ma/s) = "); + for (int uid = 0; uid < score_kernel_config.micro_batch; uid++) fprintf(stderr, "%11.2f", kernel_throughput_anchors[uid]); + fprintf(stderr, " %11.2f\n", kernel_throughput_anchors[score_kernel_config.micro_batch]); + fprintf(stderr, "BW(Mpair/s)= "); + for (int uid = 0; uid < score_kernel_config.micro_batch; uid++) fprintf(stderr, "%11.2f", kernel_throughput[uid]); + fprintf(stderr, " %11.2f\n", kernel_throughput[score_kernel_config.micro_batch]); + fprintf(stderr, "----------------------------------------------------------------------------\n"); + if (kernel_mem_usage[score_kernel_config.micro_batch] > 99){ + fprintf(stderr, + "[WARNING] long segment buffer is full. Consider increase " + "long_seg_buffer_size to improve performance.\n"); + } +#endif + + return n_reads; +} + + +/* + * 1. synchronize stream and process previous batch. cleanup stream + * 2. launch kernels (asynchornizely) for the input batch + */ + +void plchain_cal_score_async(chain_read_t **reads_, int *n_read_, Misc misc, streamSetup_t stream_setup, int thread_id, void* km){ + chain_read_t* reads = *reads_; + *reads_ = NULL; + int n_read = *n_read_; + *n_read_ = 0; + + /* sync stream and process previous batch */ + int stream_id = thread_id; + if (stream_setup.streams[stream_id].busy) { + cudaStreamSynchronize(stream_setup.streams[stream_id].cudastream); + *n_read_ = plchain_post_gpu_helper(stream_setup, stream_id, misc, km); + *reads_ = stream_setup.streams[stream_id].reads; + stream_setup.streams[stream_id].busy = false; + } + +#ifdef DEBUG_PRINT + for(int uid = 0; uid < score_kernel_config.micro_batch + 1; uid++) { + kernel_mem_usage[uid] = 0; + kernel_throughput[uid] = 0; + } +#endif // DEBUG_PRINT + + + cudaEventRecord(stream_setup.streams[stream_id].startevent, + stream_setup.streams[stream_id].cudastream); + size_t total_n = 0; + for (int i = 0; i < n_read; i++) { + total_n += reads[i].n; + } // compute total_n first +#ifdef DEBUG_PRINT + fprintf(stderr, "[Info] %s (%s:%d) Launching Batch: n_read %d, total anchors %lu (mem usage: %.2f%%)\n", __func__, __FILE__, __LINE__, n_read, total_n, (float)total_n/stream_setup.max_anchors_stream*100); +#endif // DEBUG_PRINT + + // reset long seg counters + cudaMemsetAsync(stream_setup.streams[stream_id].dev_mem.d_long_seg_count, 0, sizeof(unsigned int), + stream_setup.streams[stream_id].cudastream); + cudaMemsetAsync(stream_setup.streams[stream_id].dev_mem.d_total_n_long, 0, sizeof(size_t), + stream_setup.streams[stream_id].cudastream); + stream_setup.streams[stream_id].long_mem.total_long_segs_num[0] = 0; + stream_setup.streams[stream_id].long_mem.total_long_segs_n[0] = 0; + for(int uid = 0; uid < score_kernel_config.micro_batch; uid++) { + stream_setup.streams[stream_id].host_mems[uid].long_segs_num[0] = 0; + stream_setup.streams[stream_id].host_mems[uid].index = uid; + stream_setup.streams[stream_id].host_mems[uid].griddim = 0; + stream_setup.streams[stream_id].host_mems[uid].size = 0; + stream_setup.streams[stream_id].host_mems[uid].total_n = 0; + stream_setup.streams[stream_id].host_mems[uid].cut_num = 0; + } + + stream_setup.streams[stream_id].reads = reads; + stream_setup.streams[stream_id].n_read = n_read; + int read_start = 0; + + for (int uid = 0; uid < score_kernel_config.micro_batch; uid++) { + if (read_start == n_read) continue; +#ifdef USEHIP + roctxRangePushA("microbatch"); +#endif + // decide the size of micro batch + size_t batch_n = 0; + int read_end = 0; + size_t cut_num = 0; + int griddim = 0; + for (read_end = read_start; read_end < n_read; read_end++) { + if (batch_n + reads[read_end].n > stream_setup.max_anchors_stream) { + break; + } + batch_n += reads[read_end].n; + int an_p_block = range_kernel_config.anchor_per_block; + int an_p_cut = range_kernel_config.blockdim; + int block_num = (reads[read_end].n - 1) / an_p_block + 1; + griddim += block_num; + cut_num += (reads[read_end].n - 1) / an_p_cut + 1; + } +#ifdef DEBUG_VERBOSE + fprintf(stderr, "[Debug] %s (%s:%d) MICROBATCH#%d batch_n %lu, read_start %d, read_end %d usage %.2f %%\n", __func__, __FILE__, __LINE__, uid, batch_n, read_start, read_end, (float)batch_n/stream_setup.max_anchors_stream*100); +#endif // DEBUG_VERBOS + +#ifdef DEBUG_PRINT + kernel_mem_usage[uid] = (float)batch_n/stream_setup.max_anchors_stream*100; +#endif // DEBUG_PRINT + + // sanity check + assert(stream_setup.max_anchors_stream >= batch_n); + assert(stream_setup.max_range_grid >= griddim); + assert(stream_setup.max_num_cut >= cut_num); + // work on micro batch +#ifdef USEHIP + roctxRangePushA("reorg"); +#endif + // step1: reorg input + plmem_reorg_input_arr(reads + read_start, read_end - read_start, + &stream_setup.streams[stream_id].host_mems[uid], + range_kernel_config); + // step2: copy to device +#ifdef USEHIP + roctxRangePop(); +#endif + plmem_async_h2d_short_memcpy(&stream_setup.streams[stream_id], uid); + // step3: range selection +#ifdef DEBUG_PRINT + cudaEventRecord(stream_setup.streams[stream_id].short_kernel_start_event[uid], + stream_setup.streams[stream_id].cudastream); +#endif // DEBUG_PRINT + plrange_async_range_selection(&stream_setup.streams[stream_id].dev_mem, + &stream_setup.streams[stream_id].cudastream); + // step4: score generation for short and mid segs + plscore_async_short_mid_forward_dp(&stream_setup.streams[stream_id].dev_mem, + &stream_setup.streams[stream_id].cudastream); +#ifdef DEBUG_PRINT + cudaEventRecord(stream_setup.streams[stream_id].short_kernel_stop_event[uid], + stream_setup.streams[stream_id].cudastream); +#endif // DEBUG_PRINT + // step5: copy short and mid results back + plmem_async_d2h_short_memcpy(&stream_setup.streams[stream_id], uid); + // update index + read_start = read_end; + +#ifdef USEHIP + roctxRangePop(); +#endif + +#ifdef DEBUG_CHECK + planalyze_short_kernel(stream_setup.streams[stream_id], uid, kernel_throughput); +#endif // DEBUG_CHECK + } + +// FIXME: temporary solution for microbatching + if (read_start < n_read) { + fprintf(stderr, "[WARNING] Unable to fit reads %d - %d into a microbatch. Fall back to cpu chaining\n", read_start, n_read-1); + } + + // step6: copy back long_segs_og + cudaStreamSynchronize(stream_setup.streams[stream_id].cudastream); + unsigned int num_long_seg; + cudaMemcpy(&num_long_seg, stream_setup.streams[stream_id].dev_mem.d_long_seg_count, sizeof(unsigned int), + cudaMemcpyDeviceToHost); + seg_t* long_segs_og = (seg_t*)malloc(sizeof(seg_t) * num_long_seg); + cudaMemcpy(long_segs_og, stream_setup.streams[stream_id].dev_mem.d_long_seg_og, sizeof(seg_t) * num_long_seg, + cudaMemcpyDeviceToHost); + + // step7: sort long segs in descent order + unsigned *map = new unsigned[num_long_seg]; + for (unsigned i = 0; i < num_long_seg; i++) { + map[i] = i; + } + pairsort(long_segs_og, map, num_long_seg); + #ifdef DEBUG_VERBOSE + auto last_length = long_segs_og[map[0]].end_idx - long_segs_og[map[0]].start_idx; + for (int i = 1; i < num_long_seg; i++){ + auto this_length = long_segs_og[map[i]].end_idx - long_segs_og[map[i]].start_idx; + if (this_length > last_length) + fprintf(stderr, "Failed sort at: %d - %u\n", i, map[i]); + } + #endif // DEBUG_VERBOSE + free(long_segs_og); + + // step8: copy map to device + cudaMalloc(&stream_setup.streams[stream_id].dev_mem.d_map, sizeof(unsigned) * num_long_seg); + cudaMemcpy(stream_setup.streams[stream_id].dev_mem.d_map, map, sizeof(unsigned) * num_long_seg, cudaMemcpyHostToDevice); + free(map); + + cudaEventRecord(stream_setup.streams[stream_id].long_kernel_event, + stream_setup.streams[stream_id].cudastream); + plscore_async_long_forward_dp(&stream_setup.streams[stream_id].dev_mem, + &stream_setup.streams[stream_id].cudastream); + cudaEventRecord(stream_setup.streams[stream_id].stopevent, + stream_setup.streams[stream_id].cudastream); + plmem_async_d2h_long_memcpy(&stream_setup.streams[stream_id]); + stream_setup.streams[stream_id].busy = true; + cudaCheck(); +} + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +void init_stream_gpu(size_t* total_n, int* max_reads, int *min_n, char gpu_config_file[], Misc misc) { + plmem_stream_initialize(total_n, max_reads, min_n, gpu_config_file); + plrange_upload_misc(misc); + plscore_upload_misc(misc); +#ifdef DEBUG_PRINT + fprintf(stderr, "[Info::%s] gpu initialized for chaining with config %s\n", __func__, gpu_config_file); + fprintf(stderr, "[Info::%s] Compile time config: \n", __func__); +#ifdef USEHIP + fprintf(stderr, "\t\t USE HIP\n"); +#else + fprintf(stderr, "\t\t USE CUDA\n"); +#endif // USEHIP +#ifdef MAX_MICRO_BATCH + fprintf(stderr, "\t\t MAX MICRO BATCH \t%d\n", MAX_MICRO_BATCH); +#endif // MAX_MICRO_BATCH +#endif // DEBUG_PRINT +} + +/** + * worker for launching forward chaining on gpu (streaming) + * use KMALLOC and kfree for cpu memory management + * [in/out] in_arr_: ptr to array of reads, updated to a batch launched in previous run + * (NULL if no finishing batch) + * [in/out] n_read_: ptr to num of reads in array, updated to a batch launched in previous run + * (NULL if no finishing batch) +*/ +void chain_stream_gpu(const mm_idx_t *mi, const mm_mapopt_t *opt, chain_read_t **in_arr_, int *n_read_, + int thread_id, void* km) { + // assume only one seg. and qlen_sum desn't matter + assert(opt->max_frag_len <= 0); + Misc misc = build_misc(mi, opt, 0, 1); + plchain_cal_score_async(in_arr_, n_read_, misc, stream_setup, thread_id, km); + if (in_arr_) { + int n_read = *n_read_; + chain_read_t* out_arr = *in_arr_; + for (int i = 0; i < n_read; i++) { + post_chaining_helper(mi, opt, &out_arr[i], misc, km); + } + } +} + + +/** + * worker for finish all forward chaining kernenls on gpu + * use KMALLOC and kfree for cpu memory management + * [out] batches: array of batches + * [out] num_reads: array of number of reads in each batch + */ +void finish_stream_gpu(const mm_idx_t *mi, const mm_mapopt_t *opt, chain_read_t** reads_, + int* n_read_, int t, void* km) { + // assume only one seg. and qlen_sum desn't matter + assert(opt->max_frag_len <= 0); + Misc misc = build_misc(mi, opt, 0, 1); + /* Sync all the pending batches + backtracking */ + if (!stream_setup.streams[t].busy) { + *reads_ = NULL; + *n_read_ = 0; + return; + } + + chain_read_t* reads; + int n_read = 0; + cudaStreamSynchronize(stream_setup.streams[t].cudastream); + cudaCheck(); + + n_read = plchain_post_gpu_helper(stream_setup, t, misc, km); + reads = stream_setup.streams[t].reads; + stream_setup.streams[t].busy = false; + + for (int i = 0; i < n_read; i++) { + post_chaining_helper(mi, opt, &reads[i], misc, km); + } + + *reads_ = reads; + *n_read_ = n_read; + +} + + +void free_stream_gpu(int n_threads){ + plmem_stream_cleanup(); + + size_t gpu_free_mem, gpu_total_mem; + cudaMemGetInfo(&gpu_free_mem, &gpu_total_mem); +#ifdef DEBUG_PRINT + fprintf(stderr, "[Info] GPU free mem: %f GB, total mem: %f GB (after cleanup) \n", (float)gpu_free_mem / OneG, (float)gpu_total_mem / OneG); +#endif +} + +#ifdef __cplusplus +} // extern "C" +#endif // __cplusplus \ No newline at end of file diff --git a/gpu/plchain.h b/gpu/plchain.h new file mode 100644 index 00000000..254f1f42 --- /dev/null +++ b/gpu/plchain.h @@ -0,0 +1,25 @@ +#ifndef _PLCHAIN_H_ +#define _PLCHAIN_H_ + +/* Range Kernel configuaration */ +typedef struct range_kernel_config_t { + int blockdim; // number of threads in each block + int cut_check_anchors; // number of anchors to check around each cut + int anchor_per_block; // number of anchors assgined to one block = max_it * blockdim +} range_kernel_config_t; + +/* Score Generation Kernel configuration */ +typedef struct score_kernel_config_t{ + int micro_batch; + int short_blockdim; + int long_blockdim; + int mid_blockdim; + int short_griddim; + int long_griddim; + int mid_griddim; + int cut_unit; + int long_seg_cutoff; + int mid_seg_cutoff; +} score_kernel_config_t; + +#endif // _PLCHAIN_H_ \ No newline at end of file diff --git a/gpu/plmem.cu b/gpu/plmem.cu new file mode 100644 index 00000000..7ae07c84 --- /dev/null +++ b/gpu/plmem.cu @@ -0,0 +1,642 @@ +/* GPU memory management */ + + +#include +#include +#include +#include +#include "plmem.cuh" +#include "plrange.cuh" +#include "plscore.cuh" +#include +void plmem_malloc_host_mem(hostMemPtr *host_mem, size_t anchor_per_batch, + int range_grid_size, size_t buffer_size_long) { +#ifdef DEBUG_PRINT + size_t host_mem_size; + host_mem_size = anchor_per_batch * (sizeof(int32_t) + sizeof(int32_t) + + sizeof(int8_t) + sizeof(int32_t) + sizeof(int32_t) + sizeof(uint16_t)); + host_mem_size += range_grid_size * (sizeof(size_t) + sizeof(size_t) + sizeof(size_t)); + fprintf(stderr, "[Info] Host Malloc Pinned Memory Size %.2f GB\n", (float)host_mem_size / OneG); +#endif + + // data array + cudaMallocHost((void**)&host_mem->ax, anchor_per_batch * sizeof(int32_t)); + cudaMallocHost((void**)&host_mem->ay, anchor_per_batch * sizeof(int32_t)); + cudaMallocHost((void**)&host_mem->sid, anchor_per_batch * sizeof(int8_t)); + cudaMallocHost((void**)&host_mem->xrev, anchor_per_batch * sizeof(int32_t)); + cudaMallocHost((void**)&host_mem->f, anchor_per_batch * sizeof(int32_t)); + cudaMallocHost((void**)&host_mem->p, anchor_per_batch * sizeof(uint16_t)); + + //index + cudaMallocHost((void**)&host_mem->start_idx, range_grid_size * sizeof(size_t)); + cudaMallocHost((void**)&host_mem->read_end_idx, range_grid_size * sizeof(size_t)); + cudaMallocHost((void**)&host_mem->cut_start_idx, range_grid_size * sizeof(size_t)); + + cudaMallocHost((void**)&host_mem->long_segs_num, sizeof(unsigned int)); + cudaCheck(); +} + +void plmem_malloc_long_mem(longMemPtr *long_mem, size_t buffer_size_long) { +#ifdef DEBUG_PRINT + size_t host_mem_size; + host_mem_size = buffer_size_long / (score_kernel_config.long_seg_cutoff * score_kernel_config.cut_unit) * sizeof(seg_t); + host_mem_size += buffer_size_long * (sizeof(int32_t) + sizeof(uint16_t)); + fprintf(stderr, "[Info] Host Malloc Pinned Memory Size %.2f GB (long seg)\n", (float)host_mem_size / OneG); +#endif + // data array + cudaMallocHost((void**)&long_mem->long_segs_og_idx, buffer_size_long / (score_kernel_config.long_seg_cutoff * score_kernel_config.cut_unit) * sizeof(seg_t)); + cudaMallocHost((void**)&long_mem->f_long, buffer_size_long * sizeof(int32_t)); + cudaMallocHost((void**)&long_mem->p_long, buffer_size_long * sizeof(uint16_t)); + cudaMallocHost((void**)&long_mem->total_long_segs_num, sizeof(unsigned int)); + cudaMallocHost((void**)&long_mem->total_long_segs_n, sizeof(size_t)); + cudaCheck(); +} + +void plmem_free_host_mem(hostMemPtr *host_mem) { + cudaFreeHost(host_mem->ax); + cudaFreeHost(host_mem->ay); + cudaFreeHost(host_mem->sid); + cudaFreeHost(host_mem->xrev); + cudaFreeHost(host_mem->f); + cudaFreeHost(host_mem->p); + cudaFreeHost(host_mem->start_idx); + cudaFreeHost(host_mem->read_end_idx); + cudaFreeHost(host_mem->cut_start_idx); + cudaFreeHost(host_mem->long_segs_num); + cudaCheck(); +} + +void plmem_free_long_mem(longMemPtr *long_mem) { + cudaFreeHost(long_mem->long_segs_og_idx); + cudaFreeHost(long_mem->f_long); + cudaFreeHost(long_mem->p_long); + cudaFreeHost(long_mem->total_long_segs_num); + cudaFreeHost(long_mem->total_long_segs_n); + cudaCheck(); +} + +void plmem_malloc_device_mem(deviceMemPtr *dev_mem, size_t anchor_per_batch, int range_grid_size, int num_cut){ + // data array + cudaMalloc(&dev_mem->d_ax, anchor_per_batch * sizeof(int32_t)); + cudaMalloc(&dev_mem->d_ay, anchor_per_batch * sizeof(int32_t)); + cudaMalloc(&dev_mem->d_sid, anchor_per_batch * sizeof(int8_t)); + cudaMalloc(&dev_mem->d_xrev, anchor_per_batch * sizeof(int32_t)); + cudaMalloc(&dev_mem->d_range, anchor_per_batch * sizeof(int32_t)); + cudaMalloc(&dev_mem->d_f, anchor_per_batch * sizeof(int32_t)); + cudaMalloc(&dev_mem->d_p, anchor_per_batch * sizeof(uint16_t)); + + //index + cudaMalloc(&dev_mem->d_start_idx, range_grid_size * sizeof(size_t)); + cudaMalloc(&dev_mem->d_read_end_idx, range_grid_size * sizeof(size_t)); + cudaMalloc(&dev_mem->d_cut_start_idx, range_grid_size * sizeof(size_t)); + + // cut + cudaMalloc(&dev_mem->d_cut, num_cut * sizeof(size_t)); + cudaMalloc(&dev_mem->d_long_seg_count, sizeof(unsigned int)); + cudaMalloc(&dev_mem->d_long_seg, dev_mem->buffer_size_long / (score_kernel_config.long_seg_cutoff * score_kernel_config.cut_unit) * sizeof(seg_t)); + cudaMalloc(&dev_mem->d_long_seg_og, dev_mem->buffer_size_long / (score_kernel_config.long_seg_cutoff * score_kernel_config.cut_unit) * sizeof(seg_t)); + cudaMalloc(&dev_mem->d_mid_seg_count, sizeof(unsigned int)); + cudaMalloc(&dev_mem->d_mid_seg, num_cut/(score_kernel_config.mid_seg_cutoff + 1) * sizeof(seg_t)); + + size_t gpu_free_mem, gpu_total_mem; + cudaMemGetInfo(&gpu_free_mem, &gpu_total_mem); +#ifdef DEBUG_PRINT + fprintf(stderr, "[Info] GPU free mem: %f GB, total mem: %f GB (before alloc long seg buffer) \n", (float)gpu_free_mem / OneG, (float)gpu_total_mem / OneG); +#endif + + // long seg buffer + cudaMalloc(&dev_mem->d_ax_long, dev_mem->buffer_size_long * sizeof(int32_t)); + cudaMalloc(&dev_mem->d_ay_long, dev_mem->buffer_size_long * sizeof(int32_t)); + cudaMalloc(&dev_mem->d_sid_long, dev_mem->buffer_size_long * sizeof(int8_t)); + cudaMalloc(&dev_mem->d_range_long, dev_mem->buffer_size_long * sizeof(int32_t)); + cudaMalloc(&dev_mem->d_total_n_long, sizeof(size_t)); + cudaMalloc(&dev_mem->d_f_long, sizeof(int32_t) * dev_mem->buffer_size_long); + cudaMalloc(&dev_mem->d_p_long, sizeof(uint16_t) * dev_mem->buffer_size_long); + cudaCheck(); +} + +void plmem_free_device_mem(deviceMemPtr *dev_mem) { + cudaFree(dev_mem->d_ax); + cudaFree(dev_mem->d_ay); + cudaFree(dev_mem->d_sid); + cudaFree(dev_mem->d_xrev); + cudaFree(dev_mem->d_range); + cudaFree(dev_mem->d_f); + cudaFree(dev_mem->d_p); + + cudaFree(dev_mem->d_start_idx); + cudaFree(dev_mem->d_read_end_idx); + cudaFree(dev_mem->d_cut_start_idx); + + cudaFree(dev_mem->d_cut); + cudaFree(dev_mem->d_long_seg); + cudaFree(dev_mem->d_long_seg_count); + cudaFree(dev_mem->d_mid_seg); + cudaFree(dev_mem->d_mid_seg_count); + + cudaFree(dev_mem->d_ax_long); + cudaFree(dev_mem->d_ay_long); + cudaFree(dev_mem->d_sid_long); + cudaFree(dev_mem->d_range_long); + cudaFree(dev_mem->d_total_n_long); + cudaCheck(); +} + + +/** + * Input + * reads[]: array + * n_reads + * config: range kernel configuartions + * Output + * *host_mem populate host_mem +*/ +void plmem_reorg_input_arr(chain_read_t *reads, int n_read, + hostMemPtr *host_mem, range_kernel_config_t config) { + size_t total_n = 0, cut_num = 0; + size_t griddim = 0; + + host_mem->size = n_read; + for (int i = 0; i < n_read; i++) { + total_n += reads[i].n; + } + host_mem->total_n = total_n; + + size_t idx = 0; + for (int i = 0; i < n_read; i++) { + int n = reads[i].n; + int block_num = (n - 1) / config.anchor_per_block + 1; + + host_mem->start_idx[griddim] = idx; + size_t end_idx = idx + config.anchor_per_block; + host_mem->read_end_idx[griddim] = idx + n; + host_mem->cut_start_idx[griddim] = cut_num; + for (int j = 1; j < block_num; j++) { + cut_num += (config.anchor_per_block / config.blockdim); + host_mem->start_idx[griddim + j] = end_idx; + end_idx = + host_mem->start_idx[griddim + j] + config.anchor_per_block; + host_mem->read_end_idx[griddim + j] = idx + n; + host_mem->cut_start_idx[griddim + j] = cut_num; + } + cut_num += (n - (block_num - 1) * config.anchor_per_block - 1) / + config.blockdim; + end_idx = idx + n; + + griddim += block_num; + + for (int j = 0; j < n; j++) { + host_mem->ax[idx] = (int32_t)reads[i].a[j].x; + host_mem->ay[idx] = (int32_t)reads[i].a[j].y; + host_mem->sid[idx] = (reads[i].a[j].y & MM_SEED_SEG_MASK) >> MM_SEED_SEG_SHIFT; + host_mem->xrev[idx] = reads[i].a[j].x >> 32; + ++idx; + } + } + host_mem->cut_num = cut_num; + host_mem->griddim = griddim; +} + +void plmem_async_h2d_short_memcpy(stream_ptr_t* stream_ptrs, size_t uid) { + hostMemPtr *host_mem = &stream_ptrs->host_mems[uid]; + deviceMemPtr *dev_mem = &stream_ptrs->dev_mem; + cudaStream_t *stream = &stream_ptrs->cudastream; + cudaMemcpyAsync(dev_mem->d_ax, host_mem->ax, + sizeof(int32_t) * host_mem->total_n, cudaMemcpyHostToDevice, + *stream); + cudaMemcpyAsync(dev_mem->d_ay, host_mem->ay, + sizeof(int32_t) * host_mem->total_n, cudaMemcpyHostToDevice, + *stream); + cudaMemcpyAsync(dev_mem->d_sid, host_mem->sid, + sizeof(int8_t) * host_mem->total_n, cudaMemcpyHostToDevice, + *stream); + cudaMemcpyAsync(dev_mem->d_xrev, host_mem->xrev, + sizeof(int32_t) * host_mem->total_n, cudaMemcpyHostToDevice, + *stream); + cudaMemcpyAsync(dev_mem->d_start_idx, host_mem->start_idx, + sizeof(size_t) * host_mem->griddim, cudaMemcpyHostToDevice, + *stream); + cudaMemcpyAsync(dev_mem->d_read_end_idx, host_mem->read_end_idx, + sizeof(size_t) * host_mem->griddim, cudaMemcpyHostToDevice, + *stream); + cudaMemcpyAsync(dev_mem->d_cut_start_idx, host_mem->cut_start_idx, + sizeof(size_t) * host_mem->griddim, cudaMemcpyHostToDevice, + *stream); + cudaMemsetAsync(dev_mem->d_cut, 0xff, + sizeof(size_t) * host_mem->cut_num, *stream); + cudaMemsetAsync(dev_mem->d_f, 0, sizeof(int32_t) * host_mem->total_n, + *stream); + cudaMemsetAsync(dev_mem->d_p, 0, sizeof(uint16_t) * host_mem->total_n, + *stream); + cudaCheck(); + dev_mem->total_n = host_mem->total_n; + dev_mem->num_cut = host_mem->cut_num; + dev_mem->size = host_mem->size; + dev_mem->griddim = host_mem->griddim; +} + +void plmem_async_h2d_memcpy(stream_ptr_t* stream_ptrs) { + size_t uid = 0; + hostMemPtr *host_mem = &stream_ptrs->host_mems[uid]; + deviceMemPtr *dev_mem = &stream_ptrs->dev_mem; + cudaStream_t *stream = &stream_ptrs->cudastream; + cudaMemcpyAsync(dev_mem->d_ax, host_mem->ax, + sizeof(int32_t) * host_mem->total_n, cudaMemcpyHostToDevice, + *stream); + cudaMemcpyAsync(dev_mem->d_ay, host_mem->ay, + sizeof(int32_t) * host_mem->total_n, cudaMemcpyHostToDevice, + *stream); + cudaMemcpyAsync(dev_mem->d_sid, host_mem->sid, + sizeof(int8_t) * host_mem->total_n, cudaMemcpyHostToDevice, + *stream); + cudaMemcpyAsync(dev_mem->d_xrev, host_mem->xrev, + sizeof(int32_t) * host_mem->total_n, cudaMemcpyHostToDevice, + *stream); + cudaMemcpyAsync(dev_mem->d_start_idx, host_mem->start_idx, + sizeof(size_t) * host_mem->griddim, cudaMemcpyHostToDevice, + *stream); + cudaMemcpyAsync(dev_mem->d_read_end_idx, host_mem->read_end_idx, + sizeof(size_t) * host_mem->griddim, cudaMemcpyHostToDevice, + *stream); + cudaMemcpyAsync(dev_mem->d_cut_start_idx, host_mem->cut_start_idx, + sizeof(size_t) * host_mem->griddim, cudaMemcpyHostToDevice, + *stream); + cudaMemsetAsync(dev_mem->d_cut, 0xff, + sizeof(size_t) * host_mem->cut_num, *stream); + cudaMemsetAsync(dev_mem->d_f, 0, sizeof(int32_t) * host_mem->total_n, + *stream); + cudaMemsetAsync(dev_mem->d_p, 0, sizeof(uint16_t) * host_mem->total_n, + *stream); + cudaCheck(); + dev_mem->total_n = host_mem->total_n; + dev_mem->num_cut = host_mem->cut_num; + dev_mem->size = host_mem->size; + dev_mem->griddim = host_mem->griddim; +} + +void plmem_sync_h2d_memcpy(hostMemPtr *host_mem, deviceMemPtr *dev_mem) { + cudaMemcpy(dev_mem->d_ax, host_mem->ax, sizeof(int32_t) * host_mem->total_n, + cudaMemcpyHostToDevice); + cudaMemcpy(dev_mem->d_ay, host_mem->ay, sizeof(int32_t) * host_mem->total_n, + cudaMemcpyHostToDevice); + cudaMemcpy(dev_mem->d_sid, host_mem->sid, sizeof(int8_t) * host_mem->total_n, + cudaMemcpyHostToDevice); + cudaMemcpy(dev_mem->d_xrev, host_mem->xrev, + sizeof(int32_t) * host_mem->total_n, cudaMemcpyHostToDevice); + cudaMemcpy(dev_mem->d_start_idx, host_mem->start_idx, + sizeof(size_t) * host_mem->griddim, cudaMemcpyHostToDevice); + cudaMemcpy(dev_mem->d_read_end_idx, host_mem->read_end_idx, + sizeof(size_t) * host_mem->griddim, cudaMemcpyHostToDevice); + cudaMemcpy(dev_mem->d_cut_start_idx, host_mem->cut_start_idx, + sizeof(size_t) * host_mem->griddim, cudaMemcpyHostToDevice); + cudaMemset(dev_mem->d_cut, 0xff, sizeof(size_t) * host_mem->cut_num); + dev_mem->total_n = host_mem->total_n; + dev_mem->num_cut = host_mem->cut_num; + dev_mem->size = host_mem->size; + dev_mem->griddim = host_mem->griddim; + cudaCheck(); +} + +void plmem_async_d2h_memcpy(stream_ptr_t *stream_ptrs) { + size_t uid = 0; + hostMemPtr *host_mem = &stream_ptrs->host_mems[uid]; + longMemPtr *long_mem = &stream_ptrs->long_mem; + deviceMemPtr *dev_mem = &stream_ptrs->dev_mem; + cudaStream_t *stream = &stream_ptrs->cudastream; + cudaMemcpyAsync(host_mem->f, dev_mem->d_f, + sizeof(int32_t) * host_mem->total_n, cudaMemcpyDeviceToHost, + *stream); + cudaMemcpyAsync(host_mem->p, dev_mem->d_p, + sizeof(uint16_t) * host_mem->total_n, + cudaMemcpyDeviceToHost, *stream); + cudaMemcpyAsync(long_mem->long_segs_og_idx, dev_mem->d_long_seg_og, + dev_mem->buffer_size_long / (score_kernel_config.long_seg_cutoff * score_kernel_config.cut_unit) * sizeof(seg_t), + cudaMemcpyDeviceToHost, *stream); + cudaMemcpyAsync(host_mem->long_segs_num, dev_mem->d_long_seg_count, + sizeof(unsigned int), cudaMemcpyDeviceToHost, *stream); + cudaMemcpyAsync(long_mem->f_long, dev_mem->d_f_long, sizeof(int32_t)*dev_mem->buffer_size_long, + cudaMemcpyDeviceToHost, *stream); + cudaMemcpyAsync(long_mem->p_long, dev_mem->d_p_long, sizeof(uint16_t)*dev_mem->buffer_size_long, + cudaMemcpyDeviceToHost, *stream); + cudaCheck(); +} + +void plmem_async_d2h_short_memcpy(stream_ptr_t *stream_ptrs, size_t uid) { + hostMemPtr *host_mem = &stream_ptrs->host_mems[uid]; + deviceMemPtr *dev_mem = &stream_ptrs->dev_mem; + cudaStream_t *stream = &stream_ptrs->cudastream; + cudaMemcpyAsync(host_mem->f, dev_mem->d_f, + sizeof(int32_t) * host_mem->total_n, cudaMemcpyDeviceToHost, + *stream); + cudaMemcpyAsync(host_mem->p, dev_mem->d_p, + sizeof(uint16_t) * host_mem->total_n, + cudaMemcpyDeviceToHost, *stream); + // copy back d_long_seg_count to long_segs_num, this is an accumulative value + cudaMemcpyAsync(host_mem->long_segs_num, dev_mem->d_long_seg_count, + sizeof(unsigned int), cudaMemcpyDeviceToHost, *stream); + cudaCheck(); +} + +void plmem_async_d2h_long_memcpy(stream_ptr_t *stream_ptrs) { + size_t uid = 0; + longMemPtr *long_mem = &stream_ptrs->long_mem; + deviceMemPtr *dev_mem = &stream_ptrs->dev_mem; + cudaStream_t *stream = &stream_ptrs->cudastream; + cudaMemcpyAsync(long_mem->long_segs_og_idx, dev_mem->d_long_seg_og, + dev_mem->buffer_size_long / (score_kernel_config.long_seg_cutoff * score_kernel_config.cut_unit) * sizeof(seg_t), + cudaMemcpyDeviceToHost, *stream); + // cudaMemcpyAsync(&long_mem->total_long_segs_num, dev_mem->d_long_seg_count, + // sizeof(unsigned int), cudaMemcpyDeviceToHost, *stream); + cudaMemcpyAsync(long_mem->f_long, dev_mem->d_f_long, sizeof(int32_t)*dev_mem->buffer_size_long, + cudaMemcpyDeviceToHost, *stream); + cudaMemcpyAsync(long_mem->p_long, dev_mem->d_p_long, sizeof(uint16_t)*dev_mem->buffer_size_long, + cudaMemcpyDeviceToHost, *stream); + cudaMemcpyAsync(long_mem->total_long_segs_n, dev_mem->d_total_n_long, sizeof(size_t), + cudaMemcpyDeviceToHost, *stream); + cudaMemcpyAsync(long_mem->total_long_segs_num, dev_mem->d_long_seg_count, sizeof(unsigned int), + cudaMemcpyDeviceToHost, *stream); + cudaCheck(); +} + +void plmem_sync_d2h_memcpy(hostMemPtr *host_mem, deviceMemPtr *dev_mem){ + cudaMemcpy(host_mem->f, dev_mem->d_f, sizeof(int32_t) * host_mem->total_n, + cudaMemcpyDeviceToHost); + cudaMemcpy(host_mem->p, dev_mem->d_p, sizeof(uint16_t) * host_mem->total_n, + cudaMemcpyDeviceToHost); + cudaCheck(); +} + +//////////////////// Initialization and Cleanup //////////////////////// +streamSetup_t stream_setup; + +#include "cJSON.h" +cJSON *plmem_parse_gpu_config(const char filename[]){ + // read json file to cstring + char *buffer = 0; + long length; + FILE *f = fopen(filename, "rb"); + + if (f) { + fseek(f, 0, SEEK_END); + length = ftell(f); + fseek(f, 0, SEEK_SET); + buffer = (char*)malloc(length); + if (buffer) { + fread(buffer, 1, length, f); + } + fclose(f); + } + + if (!buffer) { + fprintf(stderr, "[Error] fail to open gpu config file %s\n", filename); + exit(1); + } + + cJSON *json = cJSON_Parse(buffer); + if (!json) { + const char *error_ptr = cJSON_GetErrorPtr(); + if (error_ptr != NULL) { + fprintf(stderr, "[Error] cJSON error before %s\n", error_ptr); + } + exit(1); + } + + return json; +} + +int get_json_int(cJSON *json, const char name[]) { + cJSON *elt = cJSON_GetObjectItem(json, name); + if (!cJSON_IsNumber(elt)) { + fprintf(stderr, "[Error] cJSON error failed to get field %s\n", name); + exit(1); + } + return elt->valueint; +} + +void plmem_config_kernels(cJSON *json) { + cJSON *range_config_json = cJSON_GetObjectItem(json, "range_kernel"); + range_kernel_config.blockdim = get_json_int(range_config_json, "blockdim"); + range_kernel_config.cut_check_anchors = + get_json_int(range_config_json, "cut_check_anchors"); + range_kernel_config.anchor_per_block = + get_json_int(range_config_json, "anchor_per_block"); + + cJSON *score_config_json = cJSON_GetObjectItem(json, "score_kernel"); + cudaDeviceProp device_prop; + cudaGetDeviceProperties(&device_prop, 0); + score_kernel_config.short_blockdim = device_prop.warpSize; + score_kernel_config.long_blockdim = device_prop.maxThreadsPerBlock; + score_kernel_config.mid_blockdim = + get_json_int(score_config_json, "mid_blockdim"); + score_kernel_config.short_griddim = + get_json_int(score_config_json, "short_griddim"); + score_kernel_config.long_griddim = + get_json_int(score_config_json, "long_griddim"); + score_kernel_config.mid_griddim = + get_json_int(score_config_json, "mid_griddim"); + score_kernel_config.long_seg_cutoff = + get_json_int(score_config_json, "long_seg_cutoff"); + score_kernel_config.mid_seg_cutoff = + get_json_int(score_config_json, "mid_seg_cutoff"); + score_kernel_config.cut_unit = range_kernel_config.blockdim; + score_kernel_config.micro_batch = + get_json_int(score_config_json, "micro_batch"); + if (score_kernel_config.micro_batch > MAX_MICRO_BATCH) { + fprintf(stderr, "[Error: gpu config] score_kernel:micro_batch should be less than %d\n" + "\t\t or recompile with MAX_MICRO_BATCH=%d" + , MAX_MICRO_BATCH, score_kernel_config.micro_batch); + exit(1); + } + +} + +void plmem_config_stream(size_t *max_range_grid_, size_t *max_num_cut_, size_t max_total_n, size_t max_read, size_t min_n){ + size_t max_range_grid, max_num_cut; + max_range_grid = + (max_total_n - 1) / range_kernel_config.anchor_per_block + 1 + max_read; + max_num_cut = (max_total_n - 1) / range_kernel_config.blockdim + 1 + max_read; + *max_range_grid_ = max_range_grid; + *max_num_cut_ = max_num_cut; + + cudaDeviceProp prop; + cudaGetDeviceProperties(&prop, 0); + cudaCheck(); + + if (*max_range_grid_ > prop.maxGridSize[0]) { + fprintf(stderr, "Invalid memory config!\n"); + exit(1); + } + +} + + +template +void plmem_config_batch(cJSON *json, int *num_stream_, + int *min_n_, size_t *max_total_n_, + int *max_read_, size_t *long_seg_buffer_size_) { + if (is_blooking) + *num_stream_ = 16; + else + *num_stream_ = get_json_int(json, "num_streams"); + + size_t min_anchors = get_json_int(json, "min_n"); + *min_n_ = min_anchors; + + /* If Use define max_total_n & max_read */ + // FIXME: this is limited by int32max + cJSON *max_total_n_json = cJSON_GetObjectItem(json, "max_total_n"); + cJSON *max_read_json = cJSON_GetObjectItem(json, "max_read"); + cJSON *long_seg_buffer_size_json = cJSON_GetObjectItem(json, "long_seg_buffer_size"); + if (max_total_n_json && max_read_json){ + *max_total_n_ = (size_t) max_total_n_json->valuedouble; + *max_read_ = max_read_json->valueint; + *long_seg_buffer_size_ = long_seg_buffer_size_json->valueint; + return; + } + + /* Determine configuration smartly */ + cudaDeviceProp prop; + cudaGetDeviceProperties(&prop, 0); + + size_t avail_mem_per_stream = (prop.totalGlobalMem / *num_stream_ ) * 0.9; + + // memory per anchor = (ax + ay + range + f + p) + (start_idx + read_end_idx + // + cut_start_idx) + cut + long_seg size: F1 = ax + ay + range + f + p; F2 + // = start_idx + read_end_idx + cut_start_idx; F3 = cut; F4 = long_seg + int F1 = 8 + 8 + 4 + 4 + 2, F2 = 8 + 8 + 8, F3 = 8, F4 = 16; + // TODO: define these data types + + // max iteration of each block, must be an integer + // int max_it = + // range_kernel_config.anchor_per_block / range_kernel_config.blockdim; + // int blockdim = range_kernel_config.blockdim; + + // g = max_grid_size + // g * F2 + g * blockdim * max_it * F1 + max_cut * F3 + max_cut/2 * F4 < + // mem_per_stream max_cut = g * max_it + /* + size_t cost_per_anchor = F1; + size_t cost_per_grid = F2; + size_t cost_per_cut = F3 + F4 / 2; + */ + size_t avg_read_n = get_json_int(json, "avg_read_n"); + + /** + * Assume max_total_n = max_read * avg_read_n + * max_grid = max_total_n / range.anchor_per_block + max_read + * = max_read *( avg_read_n / anchor_per_block + 1) + * max_cut = max_total_n / range.blockdim + max_read + * = max_read * ( avg_read_n / blockdim + 1) + * total_mem = max_grid * cost_per_grid + max_cut * cost_per_cut + max_total_n * cost_per_anchor + */ + + float grid_cost_per_read = + (avg_read_n / (float)range_kernel_config.anchor_per_block + 1) * F2; + float cut_cost_per_read = + (avg_read_n / (float)range_kernel_config.blockdim + 1) * (F3 + F4 / 2); + *max_read_ = floor(avail_mem_per_stream / + (grid_cost_per_read + cut_cost_per_read + F1 * avg_read_n)); + *max_total_n_ = *max_read_ * avg_read_n; +} + +// intialize and config kernels for gpu blocking setup +void plmem_initialize(size_t *max_total_n_, int *max_read_, + int *min_anchors_) { +#ifndef GPU_CONFIG + cJSON *json = plmem_parse_gpu_config("gpu_config.json"); +#else + cJSON *json = plmem_parse_gpu_config(GPU_CONFIG); +#endif + plmem_config_kernels(json); + int num_streams; + size_t buffer_size_long; + plmem_config_batch(json, &num_streams, min_anchors_, max_total_n_, + max_read_, &buffer_size_long); +} + +// initialize global variable stream_setup +void plmem_stream_initialize(size_t *max_total_n_, + int *max_read_, int *min_anchors_, char* gpu_config_file) { + + int num_stream; + size_t max_anchors_stream, max_range_grid, max_num_cut, long_seg_buffer_size; + + cJSON *json = plmem_parse_gpu_config(gpu_config_file); + + plmem_config_kernels(json); + size_t gpu_free_mem, gpu_total_mem; + cudaMemGetInfo(&gpu_free_mem, &gpu_total_mem); + plmem_config_batch(json, &num_stream, min_anchors_, &max_anchors_stream, + max_read_, &long_seg_buffer_size); + plmem_config_stream(&max_range_grid, &max_num_cut, max_anchors_stream, + *max_read_, *min_anchors_); + + stream_setup.num_stream = num_stream; + // assert(num_stream > 1); + + stream_setup.streams = new stream_ptr_t[num_stream]; +#ifdef DEBUG_PRINT + fprintf(stderr, + "[Info] max anchors per stream: %zu, max range grid %zu " + "max_num_cut %zu long_seg_buffer_size %zu\n", + max_anchors_stream, max_range_grid, max_num_cut, + long_seg_buffer_size); +#endif // DEBUG_PRINT + + for (int i = 0; i < num_stream; i++) { + stream_setup.streams[i].busy = false; + cudaStreamCreate(&stream_setup.streams[i].cudastream); + cudaEventCreate(&stream_setup.streams[i].stopevent); + cudaEventCreate(&stream_setup.streams[i].startevent); + cudaEventCreate(&stream_setup.streams[i].long_kernel_event); + cudaCheck(); + stream_setup.streams[i].dev_mem.buffer_size_long = long_seg_buffer_size; + // one stream has multiple host mems + for (int j = 0; j < score_kernel_config.micro_batch; j++) { + plmem_malloc_host_mem(&stream_setup.streams[i].host_mems[j], max_anchors_stream, + max_range_grid, long_seg_buffer_size); + cudaEventCreate(&stream_setup.streams[i].short_kernel_start_event[j]); + cudaEventCreate(&stream_setup.streams[i].short_kernel_stop_event[j]); + } + // one stream has one long mem and one device mem + plmem_malloc_long_mem(&stream_setup.streams[i].long_mem, long_seg_buffer_size); + plmem_malloc_device_mem(&stream_setup.streams[i].dev_mem, max_anchors_stream, + max_range_grid, max_num_cut); + cudaMemset(stream_setup.streams[i].dev_mem.d_long_seg_count, 0, sizeof(unsigned int)); + cudaCheck(); + cudaMemset(stream_setup.streams[i].dev_mem.d_mid_seg_count, 0, sizeof(unsigned int)); + cudaCheck(); + cudaMemset(stream_setup.streams[i].dev_mem.d_total_n_long, 0, sizeof(size_t)); + cudaCheck(); + } + +cudaMemGetInfo(&gpu_free_mem, &gpu_total_mem); +#ifdef DEBUG_PRINT + fprintf(stderr, "[Info] GPU free mem: %f GB, total mem: %f GB\n", (float)gpu_free_mem / OneG, (float)gpu_total_mem / OneG); +#endif + + *max_total_n_ = max_anchors_stream * score_kernel_config.micro_batch; + *max_read_ = *max_read_ * score_kernel_config.micro_batch; + + stream_setup.max_anchors_stream = max_anchors_stream; + stream_setup.max_range_grid = max_range_grid; + stream_setup.max_num_cut = max_num_cut; + stream_setup.long_seg_buffer_size_stream = long_seg_buffer_size; +} + +void plmem_stream_cleanup() { + for (int i = 0; i < stream_setup.num_stream; i++) { + cudaStreamDestroy(stream_setup.streams[i].cudastream); + cudaEventDestroy(stream_setup.streams[i].stopevent); + cudaEventDestroy(stream_setup.streams[i].startevent); + cudaEventDestroy(stream_setup.streams[i].long_kernel_event); + cudaCheck(); + // free multiple host mems + for (int j = 0; j < score_kernel_config.micro_batch; j++) { + plmem_free_host_mem(&stream_setup.streams[i].host_mems[j]); + } + plmem_free_long_mem(&stream_setup.streams[i].long_mem); + plmem_free_device_mem(&stream_setup.streams[i].dev_mem); + } + delete[] stream_setup.streams; +} diff --git a/gpu/plmem.cuh b/gpu/plmem.cuh new file mode 100644 index 00000000..c6500b5d --- /dev/null +++ b/gpu/plmem.cuh @@ -0,0 +1,147 @@ +#ifndef _PLMEM_CUH_ +#define _PLMEM_CUH_ +#include "hipify.cuh" +#include "plchain.h" +#include "plutils.h" + +#ifndef MAX_MICRO_BATCH +#define MAX_MICRO_BATCH 8 +#endif // MAX_MICRO_BATCH + +#define OneK 1024 +#define OneM (OneK*1024) +#define OneG (OneM*1024) + + +typedef struct { + int index; // read index / batch index + int griddim; // grid for range selection kernel. + int size; // number of reads in the batch + size_t total_n; // number of anchors in the batch + size_t cut_num; // number of cuts in the batch + + // array size: number of anchors in the batch + int32_t *ax; // (int32_t) a[].x + int32_t *ay; // (int32_t) a[].y + int8_t* sid; // a[].y >> 40 & 0xff + int32_t *xrev; // a[].x >> 32 + // outputs + int32_t *f; // score + uint16_t *p; // predecessor + + // array size: number of cuts in the batch / long_seg_cut + // total long segs number till this batch + unsigned int *long_segs_num; + + // start index for each block in range selection + /***** range selection block assiagnment + * One block only gets assgined one read or part of one read. + * start_idx: idx of the first anchor assigned to each block + * read_end_idx: idx of the last anchor OF THE READ assigned to each + * block if a read is devided into several blocks, all the blocks take the + * last anchor index of the read cut_start_idx: idx of the first cut this + * block needs to make + */ + // array size: grid dimension + size_t *start_idx; + size_t *read_end_idx; + size_t *cut_start_idx; +} hostMemPtr; + +typedef struct { + // array size: number of cuts in the batch / long_seg_cut + seg_t *long_segs_og_idx; // start & end idx of long segs in the original micro batch + unsigned int *total_long_segs_num; // sum of mini batch long_segs_num + size_t *total_long_segs_n; // number of anchors in all the long segs + int32_t *f_long; // score for long segs + uint16_t *p_long; // predecessor for long segs +} longMemPtr; + +typedef struct { + int size; + int griddim; + size_t total_n; + size_t num_cut; + // device memory ptrs + // data array + int32_t *d_ax; + int32_t *d_ay; + int8_t *d_sid; // a[].y >> 40 & 0xff + int32_t *d_xrev; // a[].x >> 32 + int32_t *d_range; + int32_t *d_f; // score + uint16_t *d_p; // predecessor + + // range selection index + size_t *d_start_idx; + size_t *d_read_end_idx; + size_t *d_cut_start_idx; + + // cut + size_t *d_cut; // cut + unsigned int *d_long_seg_count; // total number of long seg (aggregated accross micro batches) + seg_t *d_long_seg; // start & end idx of long segs in the long seg buffer (aggregated across micro batches) + seg_t *d_long_seg_og; // start & end idx of long seg in the micro batch. (aggregated accross micro batches) + unsigned int *d_mid_seg_count; // private to micro batch + seg_t *d_mid_seg; // private to micro batch + + // long segement buffer + unsigned *d_map; + int32_t *d_ax_long, *d_ay_long; + int8_t *d_sid_long; + int32_t *d_range_long; + size_t *d_total_n_long; + size_t buffer_size_long; + int32_t *d_f_long; // score, size: buffer_size_long * sizeof(int32_t) + uint16_t *d_p_long; // predecessor, size: buffer_size_long * sizeof(uint16_t) +} deviceMemPtr; + +typedef struct stream_ptr_t{ + chain_read_t *reads; + size_t n_read; + hostMemPtr host_mems[MAX_MICRO_BATCH]; + longMemPtr long_mem; + deviceMemPtr dev_mem; + cudaStream_t cudastream; + cudaEvent_t stopevent, startevent, long_kernel_event; + cudaEvent_t short_kernel_start_event[MAX_MICRO_BATCH]; + cudaEvent_t short_kernel_stop_event[MAX_MICRO_BATCH]; + bool busy = false; +} stream_ptr_t; + +typedef struct gputSetup_t { + int num_stream; + stream_ptr_t *streams; + size_t max_anchors_stream, max_num_cut, long_seg_buffer_size_stream; + int max_range_grid; +} streamSetup_t; + +extern streamSetup_t stream_setup; + +/* memory management methods */ +// initialization and cleanup +void plmem_initialize(size_t *max_total_n, int *max_read, int *min_n); +void plmem_stream_initialize(size_t *max_total_n, int *max_read, int *min_n, char* gpu_config_file); +void plmem_stream_cleanup(); + +// alloc and free +void plmem_malloc_host_mem(hostMemPtr *host_mem, size_t anchor_per_batch, + int range_grid_size, size_t buffer_size_long); +void plmem_malloc_long_mem(longMemPtr *long_mem, size_t buffer_size_long); +void plmem_free_host_mem(hostMemPtr *host_mem); +void plmem_free_long_mem(longMemPtr *long_mem); +void plmem_malloc_device_mem(deviceMemPtr *dev_mem, size_t anchor_per_batch, + int range_grid_size, int num_cut); +void plmem_free_device_mem(deviceMemPtr *dev_mem); + +// data movement +void plmem_reorg_input_arr(chain_read_t *reads, int n_read, + hostMemPtr *host_mem, range_kernel_config_t config); +void plmem_async_h2d_memcpy(stream_ptr_t *stream_ptrs); +void plmem_async_h2d_short_memcpy(stream_ptr_t *stream_ptrs, size_t uid); +void plmem_sync_h2d_memcpy(hostMemPtr *host_mem, deviceMemPtr *dev_mem); +void plmem_async_d2h_memcpy(stream_ptr_t *stream_ptrs); +void plmem_async_d2h_short_memcpy(stream_ptr_t *stream_ptrs, size_t uid); +void plmem_async_d2h_long_memcpy(stream_ptr_t *stream_ptrs); +void plmem_sync_d2h_memcpy(hostMemPtr *host_mem, deviceMemPtr *dev_mem); +#endif // _PLMEM_CUH_ \ No newline at end of file diff --git a/gpu/plrange.cu b/gpu/plrange.cu new file mode 100644 index 00000000..b753883e --- /dev/null +++ b/gpu/plrange.cu @@ -0,0 +1,285 @@ +#include +#include +#include +#include +#include "plrange.cuh" +#include "hipify.cuh" + + +/* + +CUDA/HIP kernel for range selection using forward chaining + +*/ + +/* kernels begin */ +__constant__ int d_max_dist_x; +__constant__ int d_max_iter; +__constant__ int d_cut_check_anchors; + +inline __device__ int64_t range_binary_search(const int32_t* ax, const int32_t* rev, int64_t i, int64_t st_end){ + int64_t st_high = st_end, st_low=i; + while (st_high != st_low) { + int64_t mid = (st_high + st_low -1) / 2+1; + if (rev[i] != rev[mid] || ax[mid] > ax[i] + d_max_dist_x) { + st_high = mid -1; + } else { + st_low = mid; + } + } + return st_high; +} + + +/** + * Forward Range Selection Kernel using global memory and binary range search. + * cut reads into segements where successor range = 0. +*/ +__global__ void range_selection_kernel_binary(const int32_t* ax, const int32_t* rev, size_t *start_idx_arr, size_t *read_end_idx_arr, + int32_t *range, size_t* cut, size_t* cut_start_idx, size_t total_n, range_kernel_config_t config){ + int tid = threadIdx.x; + int bid = blockIdx.x; + + size_t start_idx = start_idx_arr[bid]; + size_t read_end_idx = read_end_idx_arr[bid]; + size_t end_idx = start_idx + config.anchor_per_block; + end_idx = end_idx > read_end_idx ? read_end_idx : end_idx; + size_t cut_idx = cut_start_idx[bid]; + if(tid == 0 && (bid == 0 || read_end_idx_arr[bid-1] != read_end_idx)){ + cut[cut_idx] = start_idx; + } + cut_idx++; + int range_op[3] = {16, 512, 5000}; // Range Options + range_op[2] = d_max_iter; + for (size_t i = start_idx + tid; i < end_idx; i += blockDim.x) { + size_t st_max = i + d_max_iter; + st_max = st_max < read_end_idx ? st_max : read_end_idx -1; + size_t st; + for (int j=0; j<3; ++j){ + st = i + range_op[j]; + st = st <= st_max ? st : st_max; + assert(st < total_n); + assert(i < total_n); + if (st > i && (rev[st] != rev[i] || ax[st] > ax[i] + d_max_dist_x)){ + break; + } + } + st = range_binary_search(ax, rev, i, st); + range[i] = st - i; + + if (tid >= blockDim.x - d_cut_check_anchors && + blockDim.x - tid + i <= end_idx) { + if (st == i) cut[cut_idx] = i+1; + } + cut_idx++; + } +} + +/** + * Forward Range Selection Kernel using global memory and linear range search. + * cut reads into segements where successor range = 0. + */ +__global__ void range_selection_kernel_naive(const int32_t* ax, const int32_t* rev, size_t *start_idx_arr, size_t *read_end_idx_arr, + int32_t *range, size_t* cut, size_t* cut_start_idx, size_t total_n, range_kernel_config_t config){ + int tid = threadIdx.x; + int bid = blockIdx.x; + + size_t start_idx = start_idx_arr[bid]; + size_t read_end_idx = read_end_idx_arr[bid]; + size_t end_idx = start_idx + config.anchor_per_block; + end_idx = end_idx > read_end_idx ? read_end_idx : end_idx; + assert(end_idx == (bid +1 < gridDim.x) ? start_idx_arr[bid+1]: total_n); + // if(end_idx_ref != end_idx){ + // if (tid == 0){ + // int grimdim = gridDim.x; + // printf("start idx %d anchor_per_block %d read_end_idx %d, next start idx %d gridDim %d bid %d\n", + // start_idx, config.anchor_per_block, read_end_idx, start_idx_arr[bid+1], grimdim, bid); + // } + // } + // __syncthreads(); + + size_t cut_idx = cut_start_idx[bid]; + if(tid == 0 && (bid == 0 || read_end_idx_arr[bid-1] != read_end_idx)){ + cut[cut_idx] = start_idx; + } + cut_idx++; + for (size_t i = start_idx + tid; i < end_idx; i += blockDim.x){ + size_t st = i + d_max_iter; + st = i + d_max_iter < read_end_idx ? st : read_end_idx -1; + assert(st < total_n); + assert(i < total_n); + while (st > i && + (rev[i] != rev[st] // NOTE: different prefix cannot become predecessor + || ax[st] > ax[i] + d_max_dist_x)) { // NOTE: same prefix compare the value + --st; + } + range[i] = st - i; + + if (tid >= blockDim.x - d_cut_check_anchors && blockDim.x - tid + i <= end_idx) { + if (st == i) cut[cut_idx] = i+1; + } + cut_idx++; + } +} + +// __global__ void range_selection_kernel(const int64_t* ax, size_t *start_idx_arr, size_t *read_end_idx_arr, int32_t *range){ +// int tid = threadIdx.x; +// int bid = blockIdx.x; + +// size_t start_idx = start_idx_arr[bid]; +// size_t read_end_idx = read_end_idx_arr[bid]; +// size_t end_idx = start_idx + MAX_ANCHOR_PER_BLOCK; +// end_idx = end_idx > read_end_idx ? read_end_idx : end_idx; + +// size_t load_anchor_idx = 100; +// size_t load_smem_idx; +// size_t cal_idx = start_idx + threadIdx.x; +// int32_t cal_smem = tid; +// __shared__ int64_t smem[NUM_ANCHOR_IN_SMEM]; + +// /* prefetch anchors */ +// load_smem_idx = tid; +// load_anchor_idx = start_idx + tid; +// // if (tid == 20) printf("load_smem_idx %d, load_anchor_idx %lu\n", load_smem_idx, load_anchor_idx); +// for (int i = 0; i < PREFETCH_ANCHORS_RANGE/NUM_THREADS_RANGE && load_anchor_idx < read_end_idx; ++i){ +// // if (tid == 20) printf("load_smem_idx %d, load_anchor_idx %lu\n", load_smem_idx, load_anchor_idx); +// smem[load_smem_idx] = ax[load_anchor_idx]; +// load_smem_idx += NUM_THREADS_RANGE; +// load_anchor_idx += NUM_THREADS_RANGE; +// } + +// int iter = (NUM_ANCHOR_IN_SMEM - PREFETCH_ANCHORS_RANGE)/NUM_THREADS_RANGE; // iterations before another load is needed +// while (cal_idx < end_idx) { // tail threads may skip this loop +// /* load anchors */ +// load_smem_idx = load_smem_idx >= NUM_ANCHOR_IN_SMEM ? load_smem_idx - NUM_ANCHOR_IN_SMEM : load_smem_idx; +// for (int i = 0; i < iter && load_anchor_idx < end_idx + PREFETCH_ANCHORS_RANGE; ++i){ +// // if (tid == 20) printf("load it load_smem_idx %d, load_anchor_idx %lu\n", load_smem_idx, load_anchor_idx); +// smem[load_smem_idx] = ax[load_anchor_idx]; +// load_smem_idx += NUM_THREADS_RANGE; +// load_anchor_idx += NUM_THREADS_RANGE; +// load_smem_idx = load_smem_idx >= NUM_ANCHOR_IN_SMEM ? load_smem_idx - NUM_ANCHOR_IN_SMEM : load_smem_idx; +// } + +// __syncthreads(); + +// /* calculate sucessor range */ +// for (int i = 0; i < iter && cal_idx < end_idx; ++i){ +// int64_t anchor = smem[cal_smem]; + +// size_t st = cal_idx + PREFETCH_ANCHORS_RANGE < read_end_idx ? cal_idx + PREFETCH_ANCHORS_RANGE : read_end_idx-1; +// int32_t st_smem = cal_smem + st - cal_idx; +// st_smem = st_smem >= NUM_ANCHOR_IN_SMEM ? st_smem - NUM_ANCHOR_IN_SMEM : st_smem; +// // if (tid == 20) printf("cal idx %lu, cal_mem %d, st %lu, st_smem %d\n", cal_idx, cal_smem, st,st_smem); + +// // if (tid == 20) printf("anchor.x %d, smem[st_smem] %d, anchor.x+MAX_DIST_X%d\n", anchor, smem[st_smem], anchor+MAX_DIST_X); + +// while (st > cal_idx && +// (anchor>> 32 != smem[st_smem] >> 32 || +// smem[st_smem] > anchor + d_max_dist_x +// ) +// ){ +// // if (bid == 25) +// // printf("while 0 bid %d tid %d cal_idx %d\n", bid, tid, cal_idx); +// --st; +// if (st_smem == 0) st_smem = NUM_ANCHOR_IN_SMEM-1; +// else --st_smem; +// } + +// /* NOTE: fallback: succussor is not prefetched */ +// if (st >= PREFETCH_ANCHORS_RANGE + cal_idx){ +// st = cal_idx + MAX_ITER < read_end_idx ? i + MAX_ITER : read_end_idx-1; +// while( +// anchor >> 32 != ax[st] >> 32 || +// ax[st] > anchor + d_max_dist_x // check from global memory +// ){ +// --st; +// // if (bid == 25) +// // printf("while 1 bid %d tid %d\n", bid, tid); +// } + +// } +// range[cal_idx] = st - cal_idx; +// cal_smem += NUM_THREADS_RANGE; +// cal_smem = cal_smem >= NUM_ANCHOR_IN_SMEM ? cal_smem - NUM_ANCHOR_IN_SMEM : cal_smem; +// cal_idx += NUM_THREADS_RANGE; +// // if (bid == 25) +// // printf("for loop i %d bid %d tid %d\n", i, bid, tid); +// } +// // if (bid == 25) +// // printf("outer while bid %d tid %d\n", bid, tid); +// __syncthreads(); + +// } + +// } + +/* kernels end */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* host functions begin */ +range_kernel_config_t range_kernel_config; + +void plrange_upload_misc(Misc misc){ +#ifdef USEHIP + hipMemcpyToSymbol(HIP_SYMBOL(d_max_dist_x), &misc.max_dist_x, sizeof(int)); + hipMemcpyToSymbol(HIP_SYMBOL(d_max_iter), &misc.max_iter, sizeof(int)); + hipMemcpyToSymbol(HIP_SYMBOL(d_cut_check_anchors), + &range_kernel_config.cut_check_anchors, sizeof(int)); +#else + cudaCheck(); + cudaMemcpyToSymbol(d_max_dist_x, &misc.max_dist_x, sizeof(int)); + cudaMemcpyToSymbol(d_max_iter, &misc.max_iter, sizeof(int)); + cudaMemcpyToSymbol(d_cut_check_anchors, + &range_kernel_config.cut_check_anchors, sizeof(int)); +#endif // USEHIP + cudaCheck(); +} + +void plrange_async_range_selection(deviceMemPtr* dev_mem, cudaStream_t* stream) { + size_t total_n = dev_mem->total_n, cut_num = dev_mem->num_cut; + int griddim = dev_mem->griddim; + dim3 DimBlock(range_kernel_config.blockdim, 1, 1); + dim3 DimGrid(griddim, 1, 1); + + // Run kernel + range_selection_kernel_binary<<>>( + dev_mem->d_ax, dev_mem->d_xrev, dev_mem->d_start_idx, dev_mem->d_read_end_idx, + dev_mem->d_range, dev_mem->d_cut, dev_mem->d_cut_start_idx, total_n, range_kernel_config); + cudaCheck(); +#ifdef DEBUG_PRINT + // fprintf(stderr, "[Info] %s (%s:%d): Batch total_n %lu, Range Kernel Launched, grid %d cut %d\n", __func__, __FILE__, __LINE__, total_n, DimGrid.x, cut_num); +#endif +} + +void plrange_sync_range_selection(deviceMemPtr *dev_mem, Misc misc) { + size_t total_n = dev_mem->total_n, cut_num = dev_mem->num_cut; + int griddim = dev_mem->griddim; + dim3 DimBlock(range_kernel_config.blockdim, 1, 1); + dim3 DimGrid(griddim,1,1); + + plrange_upload_misc(misc); + + // Run kernel +#ifdef DEBUG_PRINT + fprintf(stderr, "[Info] %s (%s:%d): Grim Dim: %d Cut: %zu Anchors: %zu\n", __func__, __FILE__, __LINE__, DimGrid.x, + cut_num, total_n); +#endif + range_selection_kernel_binary<<>>( + dev_mem->d_ax, dev_mem->d_xrev, dev_mem->d_start_idx, dev_mem->d_read_end_idx, + dev_mem->d_range, dev_mem->d_cut, dev_mem->d_cut_start_idx, total_n, range_kernel_config); + cudaCheck(); + cudaDeviceSynchronize(); + cudaCheck(); +#ifdef DEBUG_PRINT + fprintf(stderr, "[Info] %s: range calculation success\n", __func__); +#endif +} + +#ifdef __cplusplus +} +#endif + +/* host functions end */ diff --git a/gpu/plrange.cuh b/gpu/plrange.cuh new file mode 100644 index 00000000..dcfb7426 --- /dev/null +++ b/gpu/plrange.cuh @@ -0,0 +1,25 @@ +#ifndef _PLRANGE_CUH_ +#define _PLRANGE_CUH_ + +#include "plmem.cuh" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef __int32_t int32_t; + +/* functions declaration */ +void plrange_upload_misc(Misc misc); +void plrange_async_range_selection(deviceMemPtr* device_mem_ptr, cudaStream_t* stream); +void plrange_sync_range_selection(deviceMemPtr* dev_mem, Misc misc); + +extern range_kernel_config_t range_kernel_config; + + +#ifdef __cplusplus +} +#endif + +#endif // _PLRANGE_CUH_ diff --git a/gpu/plscore.cu b/gpu/plscore.cu new file mode 100644 index 00000000..58e62364 --- /dev/null +++ b/gpu/plscore.cu @@ -0,0 +1,635 @@ +#include +#include +#include +#include +#include "plscore.cuh" +#include "hipify.cuh" + +/* + +Parallel chaining helper functions with CUDA + +*/ + +__constant__ Misc misc; +__constant__ int long_seg_cutoff; +__constant__ int mid_seg_cutoff; +__device__ unsigned curr_long_segid; + +/* arithmetic functions begin */ + +// __device__ static inline float cuda_mg_log2(float x) // NB: this doesn't work when x<2 +// { +// union { float f; uint32_t i; } z = { x }; +// float log_2 = ((z.i >> 23) & 255) - 128; +// z.i &= ~(255 << 23); +// z.i += 127 << 23; +// log_2 += (-0.34484843f * z.f + 2.02466578f) * z.f - 0.67487759f; +// return log_2; +// } + +__device__ static inline float cuda_mg_log2(int32_t x) // NB: this doesn't work when x<2 +{ + return 31 - __clz(x); +} + +__device__ int32_t original_comput_sc(const int32_t ai_x, const int32_t ai_y, const int32_t aj_x, const int32_t aj_y, + const int8_t sidi, const int8_t sidj, + int32_t max_dist_x, int32_t max_dist_y, + int32_t bw, float chn_pen_gap, + float chn_pen_skip, int is_cdna, int n_seg) { + int32_t dq = ai_y - aj_y, dr, dd, dg, q_span, sc; + if (dq <= 0 || dq > max_dist_x) return INT32_MIN; + dr = ai_x - aj_x; + if (sidi == sidj && (dr == 0 || dq > max_dist_y)) return INT32_MIN; + dd = dr > dq ? dr - dq : dq - dr; + if (sidi == sidj && dd > bw) return INT32_MIN; + if (n_seg > 1 && !is_cdna && sidi == sidj && dr > max_dist_y) + return INT32_MIN; // nseg = 1 by default + dg = dr < dq ? dr : dq; + q_span = MM_QSPAN; + sc = q_span < dg ? q_span : dg; + if (dd || dg > q_span) { + float lin_pen, log_pen; + lin_pen = chn_pen_gap * (float)dd + chn_pen_skip * (float)dg; + log_pen = + dd >= 1 ? cuda_mg_log2(dd + 1) : 0.0f; // mg_log2() only works for dd>=2 + if (is_cdna || sidi != sidj) { + if (sidi != sidj && dr == 0) + ++sc; // possibly due to overlapping paired ends; give a minor + // bonus + else if (dr > dq || sidi != sidj) + sc -= + (int)(lin_pen < log_pen ? lin_pen + : log_pen); // deletion or jump + // between paired ends + else + sc -= (int)(lin_pen + .5f * log_pen); + } else + sc -= (int)(lin_pen + .5f * log_pen); + } + return sc; +} + +__device__ int32_t comput_sc(const int32_t ai_x, const int32_t ai_y, const int32_t aj_x, const int32_t aj_y, + const int8_t sidi, const int8_t sidj, + int32_t max_dist_x, int32_t max_dist_y, + int32_t bw, float chn_pen_gap, + float chn_pen_skip, int is_cdna, int n_seg) { + int32_t dq = ai_y - aj_y, dr, dd, dg, sc; + dr = ai_x - aj_x; + dd = __sad(dr, dq, 0); + + if (dq <= 0 || dq > max_dist_x || + (sidi == sidj && (dr == 0 || + dq > max_dist_y || + dd > bw || + (n_seg > 1 && !is_cdna && dr > max_dist_y)))) + return INT32_MIN; + + dg = dr < dq ? dr : dq; // dg = min(dr, dq) + sc = MM_QSPAN < dg ? MM_QSPAN : dg; // sc = min(q_span, dr, dq) + if (dd || dg > MM_QSPAN) { + int32_t log_pen = dd >= 1 ? (31 - __clz(dd+1)) : 0; + int32_t lin_pen = chn_pen_gap * (float)dd + chn_pen_skip * (float)dg; + // Initial conditions for modifying score based on penalties + bool minorBonus = is_cdna && sidi != sidj && dr == 0; + bool majorAdjustment = (is_cdna && dg == dq) || sidi != sidj; + sc += minorBonus; + sc -= (!minorBonus && majorAdjustment) * (int)(lin_pen < log_pen ? lin_pen : log_pen); + sc -= (!minorBonus && !majorAdjustment) * (int)(lin_pen + 0.5f * log_pen); + } + return sc; +} + + +/* arithmetic functions end */ + +inline __device__ void compute_sc_seg_one_wf(int32_t* anchors_x, int32_t* anchors_y, int8_t* sid, int32_t* range, + size_t start_idx, size_t end_idx, + int32_t* f, uint16_t* p +){ + Misc blk_misc = misc; + int tid = threadIdx.x; + // int bid = blockIdx.x; + // init f and p + for (size_t i=start_idx+tid; i < end_idx; i += blockDim.x) { + f[i] = MM_QSPAN; + p[i] = 0; + } + // __syncthreads(); + // assert(range[end_idx-1] == 0); + for (size_t i=start_idx; i < end_idx; i++) { + int32_t range_i = range[i]; + // if (range_i + i >= end_idx) + // printf("range_i %d i %lu start_idx %lu, end_idx %lu\n", range_i, i, start_idx, end_idx); + // assert(range_i + i < end_idx); + for (int32_t j = tid; j < range_i; j += blockDim.x) { + int32_t sc = comput_sc( + anchors_x[i+j+1], + anchors_y[i+j+1], + anchors_x[i], + anchors_y[i], + sid [i+j+1], + sid [i], + blk_misc.max_dist_x, blk_misc.max_dist_y, blk_misc.bw, blk_misc.chn_pen_gap, + blk_misc.chn_pen_skip, blk_misc.is_cdna, blk_misc.n_seg); + if (sc == INT32_MIN) continue; + sc += f[i]; + if (sc >= f[i+j+1] && sc != MM_QSPAN) { + f[i+j+1] = sc; + p[i+j+1] = j+1; + + } + } +#ifndef USEHIP + __syncwarps(); // NOTE: single warp, no need to sync +#endif // USEHIP + } + +} + + +inline __device__ void compute_sc_seg_multi_wf(const int32_t* anchors_x, const int32_t* anchors_y, const int8_t* sid, const int32_t* range, + size_t start_idx, size_t end_idx, + int32_t* f, uint16_t* p +){ + Misc blk_misc = misc; + int tid = threadIdx.x; + int bid = blockIdx.x; + // init f and p + for (size_t i=start_idx+tid; i < end_idx; i += blockDim.x) { + f[i] = MM_QSPAN; + p[i] = 0; + } + __syncthreads(); + // assert(range[end_idx-1] == 0); + for (size_t i=start_idx; i < end_idx; i++) { + int32_t range_i = range[i]; + // if (range_i + i >= end_idx) + // printf("range_i %d i %lu start_idx %lu, end_idx %lu\n", range_i, i, start_idx, end_idx); + // assert(range_i + i < end_idx); + for (int32_t j = tid; j < range_i; j += blockDim.x) { + int32_t sc = comput_sc( + anchors_x[i+j+1], + anchors_y[i+j+1], + anchors_x[i], + anchors_y[i], + sid [i+j+1], + sid [i], + blk_misc.max_dist_x, blk_misc.max_dist_y, blk_misc.bw, blk_misc.chn_pen_gap, + blk_misc.chn_pen_skip, blk_misc.is_cdna, blk_misc.n_seg); + if (sc == INT32_MIN) continue; + sc += f[i]; + if (sc >= f[i+j+1] && sc != MM_QSPAN) { + f[i+j+1] = sc; + p[i+j+1] = j+1; + + } + } + __syncthreads(); + } + +} + +#define NUM_ANCHORS_PREFETCH 1024 + +// inline __device__ void compute_sc_seg_shared(const int64_t* anchors_x, const int64_t* anchors_y, int32_t* range, +// size_t start_idx, size_t end_idx, +// int32_t* f, uint16_t* p +// ){ +// Misc blk_misc = misc; +// int tid = threadIdx.x; +// int bid = blockIdx.x; +// // init f and p +// for (size_t i=start_idx+tid; i < end_idx; i += blockDim.x) { +// f[i] = anchors_y[i] >> 32 & 0xff; +// p[i] = 0; +// } +// __syncthreads(); +// // assert(range[end_idx-1] == 0); +// __shared__ int64_t anchors_x_shared[NUM_ANCHORS_PREFETCH]; + +// __shared__ int64_t anchors_y_shared[NUM_ANCHORS_PREFETCH]; +// size_t prefetch_end_idx = 0; +// unsigned int prefetch_smem_offset = 0; +// for (size_t i = start_idx; i < end_idx; i++) { +// int32_t range_i = range[i]; +// // if (range_i + i >= end_idx) +// // printf("range_i %d i %lu start_idx %lu, end_idx %lu\n", range_i, i, start_idx, end_idx); +// // assert(range_i + i < end_idx); +// for (int32_t j = tid; j < range_i; j += blockDim.x) { +// int32_t sc = comput_sc( +// anchors_x[i+j+1], +// anchors_y[i+j+1], +// anchors_x[i], +// anchors_y[i], +// blk_misc.max_dist_x, blk_misc.max_dist_y, blk_misc.bw, blk_misc.chn_pen_gap, +// blk_misc.chn_pen_skip, blk_misc.is_cdna, blk_misc.n_seg); +// if (sc == INT32_MIN) continue; +// sc += f[i]; +// if (sc >= f[i+j+1] && sc != (anchors_y[i+j+1]>>32 & 0xff)) { +// f[i+j+1] = sc; +// p[i+j+1] = j+1; + +// } +// } +// __syncthreads(); +// } +// } + +// inline __device__ void compute_sc_long_seg_one_wf(const int64_t* anchors_x, const int64_t* anchors_y, int32_t* range, +// size_t start_idx, size_t end_idx, +// int32_t* f, uint16_t* p +// ){ +// Misc blk_misc = misc; +// int tid = threadIdx.x; +// // int bid = blockIdx.x; +// // NOTE: smallest alignd offset that is greater than start_idx +// // anchor_offset = tid; +// // while (anchor_offset <= start_idx) anchor_offset += blockDim.x; +// int anchor_offset = tid + (start_idx - tid + blockDim.x) / blockDim.x * blockDim.x; +// // init f and p +// for (size_t i=anchor_offset; i < end_idx; i += blockDim.x) { +// f[i] = anchors_y[i] >> 32 & 0xff; +// p[i] = 0; +// } +// // int64_t local_anchors[10]; +// int64_t anchor_x = anchors_x[anchor_offset]; +// int64_t anchor_y = anchors_y[anchor_offset]; +// __syncthreads(); +// // assert(range[end_idx-1] == 0); +// for (size_t i=start_idx; i < end_idx; i++) { +// int32_t range_i = range[i]; +// // if (range_i + i >= end_idx) +// // printf("range_i %d i %lu start_idx %lu, end_idx %lu\n", range_i, i, start_idx, end_idx); +// // assert(range_i + i < end_idx); +// // for (int32_t j = tid; j < range_i; j += blockDim.x) { +// for (unsigned j = anchor_offset; j < i+range_i+1; j += blockDim.x) { +// anchor_x = anchors_x[j]; +// anchor_y = anchors_y[j]; +// int32_t sc = comput_sc( +// anchor_x, +// anchor_y, +// anchors_x[i], +// anchors_y[i], +// blk_misc.max_dist_x, blk_misc.max_dist_y, blk_misc.bw, blk_misc.chn_pen_gap, +// blk_misc.chn_pen_skip, blk_misc.is_cdna, blk_misc.n_seg); +// if (sc == INT32_MIN) continue; +// sc += f[i]; +// if (sc >= f[j] && sc != (anchors_y[j]>>32 & 0xff)) { +// f[j] = sc; +// p[j] = j+1; +// } +// } +// anchor_offset += (anchor_offset <= i+1) * blockDim.x; // update anchor offset +// __syncthreads(); +// } + +// } + + + +/* kernels begin */ + + +template +__launch_bounds__(short_block_size) +__global__ void score_generation_short( + /* Input: Anchor & Range Inputs */ + int32_t* anchors_x, int32_t* anchors_y, int8_t* sid, int32_t *range, + /* Input: Segmentations */ + size_t *seg_start_arr, + /* Output: Score and Previous Anchor */ + int32_t* f, uint16_t* p, + /* Sizes*/ + size_t total_n, size_t seg_count, + /* Output: Long segs */ + int32_t* a_x_long, int32_t* a_y_long, int8_t* sid_long, int32_t* range_long, /* aggregated memory space for long seg */ + size_t* total_n_long, size_t buffer_size_long + , seg_t* long_seg, seg_t* long_seg_og, unsigned int *long_seg_count + ,seg_t *mid_seg, unsigned int *mid_seg_count){ + int tid = threadIdx.x; + int bid = blockIdx.x; + // init f and p + for(int segid = bid; segid < seg_count; segid += gridDim.x){ + size_t start_idx = seg_start_arr[segid]; + if (start_idx == SIZE_MAX) continue; // start at a failed cut: continue to next iteration + size_t end_idx = SIZE_MAX; + int end_segid = segid + 1; + while (true) { + if (end_segid >= seg_count) { + end_idx = total_n; + break; + } + if (seg_start_arr[end_segid] != SIZE_MAX) { + end_idx = seg_start_arr[end_segid]; + break; + } + ++end_segid; + } + if (end_segid > segid + long_seg_cutoff) { + size_t long_seg_start_idx; + if (tid == 0) { + /* Allocate space in long seg buffer */ + long_seg_start_idx = atomicAdd((unsigned long long int*)total_n_long, (unsigned long long int)end_idx - start_idx); + if (long_seg_start_idx + (end_idx - start_idx) >= buffer_size_long){ // long segement buffer is full + atomicSub((unsigned long long int*)total_n_long, (unsigned long long int)end_idx - start_idx); // rollback total_n_long + long_seg_start_idx = SIZE_MAX; + // fallback to mid kernel + int mid_seg_idx = atomicAdd((unsigned long long int*)mid_seg_count, 1); + mid_seg[mid_seg_idx].start_idx = start_idx; + mid_seg[mid_seg_idx].end_idx = end_idx; + } else { + int long_seg_idx = atomicAdd((unsigned long long int*)long_seg_count, 1); + long_seg[long_seg_idx].start_idx = long_seg_start_idx; + long_seg[long_seg_idx].end_idx = long_seg_start_idx + (end_idx - start_idx); + long_seg_og[long_seg_idx].start_idx = start_idx; + long_seg_og[long_seg_idx].end_idx = end_idx; + //DEBUG: used for debug plchain_cal_long_seg_range_dis LONG_SEG_RANGE_DIS + #ifdef DEBUG_VERBOSE + long_seg_og[long_seg_idx].start_segid = segid; + long_seg_og[long_seg_idx].end_segid = end_segid; + #endif // DEBUG_VERBOSE + } + } + // broadcast long_seg_start_idx to all scalar registers +#ifdef USEHIP + long_seg_start_idx = __builtin_amdgcn_readfirstlane(long_seg_start_idx); +#else + long_seg_start_idx = __shfl_sync(0xffffffff, long_seg_start_idx, 0); +#endif + if (long_seg_start_idx == SIZE_MAX) + continue; // failed to allocate long_seg buffer + for (uint64_t idx = tid; idx < end_idx - start_idx; idx += blockDim.x){ + a_x_long[long_seg_start_idx + idx] = anchors_x[start_idx + idx]; + a_y_long[long_seg_start_idx + idx] = anchors_y[start_idx + idx]; + sid_long[long_seg_start_idx + idx] = sid[start_idx + idx]; + range_long[long_seg_start_idx + idx] = range[start_idx + idx]; + // assert(long_seg_start_idx + idx < buffer_size_long); + // assert(start_idx + idx < total_n); + } + continue; + } else if (end_segid > segid + mid_seg_cutoff) { + if (tid == 0) { + int mid_seg_idx = atomicAdd(mid_seg_count, 1); + mid_seg[mid_seg_idx].start_idx = start_idx; + mid_seg[mid_seg_idx].end_idx = end_idx; + } + continue; + } + // assert(end_idx <= total_n); + compute_sc_seg_one_wf(anchors_x, anchors_y, sid, range, start_idx, end_idx, f, p); + } +} + + +template +__launch_bounds__(mid_block_size) +__global__ void score_generation_mid(int32_t* anchors_x, int32_t* anchors_y, int8_t* sid, int32_t *range, + seg_t *long_seg, unsigned int* long_seg_count, + int32_t* f, uint16_t* p){ + int tid = threadIdx.x; + int bid = blockIdx.x; + + for(int segid = bid; segid < *long_seg_count; segid += gridDim.x){ + seg_t seg = long_seg[segid]; + // compute_sc_seg_one_wf(anchors_x, anchors_y, sid, range, seg.start_idx, seg.end_idx, f, p); + compute_sc_seg_multi_wf(anchors_x, anchors_y, sid, range, seg.start_idx, seg.end_idx, f, p); + } +} + +template +__launch_bounds__(long_block_size) +__global__ void score_generation_long(int32_t* anchors_x, int32_t* anchors_y, int8_t* sid, int32_t *range, + seg_t *long_seg, unsigned int* long_seg_count, + int32_t* f, uint16_t* p){ + int tid = threadIdx.x; + int bid = blockIdx.x; + + for(int segid = bid; segid < *long_seg_count; segid += gridDim.x){ + seg_t seg = long_seg[segid]; + // compute_sc_seg_one_wf(anchors_x, anchors_y, sid, range, seg.start_idx, seg.end_idx, f, p); + compute_sc_seg_multi_wf(anchors_x, anchors_y, sid, range, seg.start_idx, seg.end_idx, f, p); + } +} + +// FIXME: merge together +template +__launch_bounds__(long_block_size) +__global__ void score_generation_long_map(int32_t* anchors_x, int32_t* anchors_y, int8_t* sid, int32_t *range, + seg_t *long_seg, unsigned int* long_seg_count, + int32_t* f, uint16_t* p, unsigned int* map){ + int tid = threadIdx.x; + int bid = blockIdx.x; + unsigned int seg_count = 0; + + // #ifdef DEBUG_CHECK + // auto start = clock64(); + // #endif + + __shared__ unsigned int segid; + if (tid == 0 && bid == 0) { + // init the first batch as the size of the grid + curr_long_segid = gridDim.x; + } + if (tid == 0) { + segid = bid; + } + + __syncthreads(); + while (segid < *long_seg_count) { + seg_t seg = long_seg[map[segid]]; // sorted + // seg_t seg = long_seg[segid]; // unsorted + compute_sc_seg_multi_wf(anchors_x, anchors_y, sid, range, seg.start_idx, seg.end_idx, f, p); + seg_count++; + if (tid == 0) segid = atomicAdd(&curr_long_segid, 1); + __syncthreads(); + } + + // for(int segid = bid; segid < *long_seg_count; segid += gridDim.x){ + // // seg_t seg = long_seg[map[segid]]; // sorted + // seg_t seg = long_seg[segid]; // unsorted + // compute_sc_seg_multi_wf(anchors_x, anchors_y, sid, range, seg.start_idx, seg.end_idx, f, p); + // seg_count++; + // } + // #ifdef DEBUG_CHECK + // auto end = clock64(); + // if (threadIdx.x == 0) { + // printf("bid: %d, long kernel time: %lu, process %u segs\n", bid, end - start, seg_count); + // } + // #endif +} + +__global__ void score_generation_naive(int32_t* anchors_x, int32_t* anchors_y, int8_t* sid, int32_t *range, + size_t *seg_start_arr, + int32_t* f, uint16_t* p, size_t total_n, size_t seg_count) { + + // NOTE: each block deal with one batch + // the number of threads in a block is fixed, so we need to calculate iter + // n = end_idx_arr - start_idx_arr + // iter = (range[i] - 1) / num_threads + 1 + + int tid = threadIdx.x; + int bid = blockIdx.x; + for (int segid = bid; segid < seg_count; segid += gridDim.x){ + /* calculate the segement for current block */ + size_t start_idx = seg_start_arr[segid]; + if (start_idx == SIZE_MAX) continue; // start at a failed cut: continue to next iteration + size_t end_idx = SIZE_MAX; + int end_segid = segid + 1; + while (true) { + if (end_segid >= seg_count) { + end_idx = total_n; + break; + } + if (seg_start_arr[end_segid] != SIZE_MAX) { + end_idx = seg_start_arr[end_segid]; + break; + } + ++end_segid; + } + // assert(end_idx <= total_n); + compute_sc_seg_one_wf(anchors_x, anchors_y, sid, range, start_idx, end_idx, f, p); + } +} + +/* kernels end */ + +/* host functions begin */ +score_kernel_config_t score_kernel_config; + +void plscore_upload_misc(Misc input_misc) { +#ifdef USEHIP + hipMemcpyToSymbol(HIP_SYMBOL(misc), &input_misc, sizeof(Misc)); + hipMemcpyToSymbol(HIP_SYMBOL(long_seg_cutoff), &score_kernel_config.long_seg_cutoff, sizeof(int)); + hipMemcpyToSymbol(HIP_SYMBOL(mid_seg_cutoff), &score_kernel_config.mid_seg_cutoff, sizeof(int)); +#else + cudaMemcpyToSymbol(misc, &input_misc, sizeof(Misc)); +#endif + cudaCheck(); +} + +void plscore_async_short_mid_forward_dp(deviceMemPtr* dev_mem, cudaStream_t* stream) { + size_t total_n = dev_mem->total_n; + size_t cut_num = dev_mem->num_cut; + size_t buffer_size_long = dev_mem->buffer_size_long; + dim3 shortDimGrid(score_kernel_config.short_griddim, 1, 1); + dim3 midDimGrid(score_kernel_config.mid_griddim, 1, 1); + dim3 shortDimBlock(score_kernel_config.short_blockdim, 1, 1); + + // Run kernel; + cudaMemsetAsync(dev_mem->d_mid_seg_count, 0, sizeof(unsigned int), + *stream); + + if (score_kernel_config.short_blockdim == 32 ){ + score_generation_short<32><<>>( + dev_mem->d_ax, dev_mem->d_ay, dev_mem->d_sid, dev_mem->d_range, + dev_mem->d_cut, dev_mem->d_f, dev_mem->d_p, total_n, cut_num, + dev_mem->d_ax_long, dev_mem->d_ay_long, dev_mem->d_sid_long, dev_mem->d_range_long, + dev_mem->d_total_n_long, buffer_size_long, + dev_mem->d_long_seg, dev_mem->d_long_seg_og, dev_mem->d_long_seg_count, + dev_mem->d_mid_seg, dev_mem->d_mid_seg_count); + } else if (score_kernel_config.short_blockdim == 64) { + score_generation_short<64><<>>( + dev_mem->d_ax, dev_mem->d_ay, dev_mem->d_sid, dev_mem->d_range, + dev_mem->d_cut, dev_mem->d_f, dev_mem->d_p, total_n, cut_num, + dev_mem->d_ax_long, dev_mem->d_ay_long, dev_mem->d_sid_long, dev_mem->d_range_long, + dev_mem->d_total_n_long, buffer_size_long, + dev_mem->d_long_seg, dev_mem->d_long_seg_og, dev_mem->d_long_seg_count, + dev_mem->d_mid_seg, dev_mem->d_mid_seg_count); + } else { + fprintf(stderr, + "[ERROR] Unsupported warpsize: %d. mm2-gb only supports device " + "with a warpsize of 32 / 64. ", + score_kernel_config.short_blockdim); + exit(1); + } + cudaCheck(); + + + if (score_kernel_config.mid_blockdim == 128){ + score_generation_mid<128><<>>( + dev_mem->d_ax, dev_mem->d_ay, dev_mem->d_sid, dev_mem->d_range, dev_mem->d_mid_seg, + dev_mem->d_mid_seg_count, dev_mem->d_f, dev_mem->d_p); + } else if (score_kernel_config.mid_blockdim == 256){ + score_generation_mid<256><<>>( + dev_mem->d_ax, dev_mem->d_ay, dev_mem->d_sid, dev_mem->d_range, dev_mem->d_mid_seg, + dev_mem->d_mid_seg_count, dev_mem->d_f, dev_mem->d_p); + } else if (score_kernel_config.mid_blockdim == 512){ + score_generation_mid<512><<>>( + dev_mem->d_ax, dev_mem->d_ay, dev_mem->d_sid, dev_mem->d_range, dev_mem->d_mid_seg, + dev_mem->d_mid_seg_count, dev_mem->d_f, dev_mem->d_p); + } else if (score_kernel_config.mid_blockdim == 1024){ + score_generation_mid<1024><<>>( + dev_mem->d_ax, dev_mem->d_ay, dev_mem->d_sid, dev_mem->d_range, dev_mem->d_mid_seg, + dev_mem->d_mid_seg_count, dev_mem->d_f, dev_mem->d_p); + } else { + fprintf(stderr, + "[ERROR] Unsupported mid_blockdim: %d. mm2-gb only supports a " + "blockdim of 128/256/512/1024 for mid kernel \n\n" + "Please adjust score_kernel:mid_blockdim in gpu config file. ", + score_kernel_config.mid_blockdim); + exit(1); + } + cudaCheck(); + +#ifdef DEBUG_PRINT + // fprintf(stderr, "[Info] %s (%s:%d) short mid score kernel launched\n", __func__, __FILE__, __LINE__); +#endif + + cudaCheck(); +} + +void plscore_async_long_forward_dp(deviceMemPtr* dev_mem, cudaStream_t* stream) { + size_t total_n = dev_mem->total_n; + size_t cut_num = dev_mem->num_cut; + size_t buffer_size_long = dev_mem->buffer_size_long; + dim3 longDimGrid(score_kernel_config.long_griddim, 1, 1); + +#ifdef DEBUG_VERBOSE + fprintf(stderr, "[Debug] %s (%s:%d) Long Grid Dim = %d\n", __func__, __FILE__, __LINE__, longDimGrid.x); +#endif // DEBUG_VERBOSE + + + if (score_kernel_config.long_blockdim == 1024){ + score_generation_long_map<1024><<>>( + dev_mem->d_ax_long, dev_mem->d_ay_long, dev_mem->d_sid_long, dev_mem->d_range_long, dev_mem->d_long_seg, + dev_mem->d_long_seg_count, dev_mem->d_f_long, dev_mem->d_p_long, dev_mem->d_map); + } else { + fprintf(stderr, + "[ERROR] Unsupported MaxThreadsPerBlock: %d. mm2-gb only supports a blockdim of 1024 for long kernel ", + score_kernel_config.long_blockdim); + exit(1); + } + + cudaCheck(); + +#ifdef DEBUG_PRINT + // fprintf(stderr, "[Info] %s (%s:%d) long score generation launched\n", __func__, __FILE__, __LINE__); +#endif + + cudaCheck(); +} + +void plscore_async_naive_forward_dp(deviceMemPtr* dev_mem, + cudaStream_t* stream) { + size_t total_n = dev_mem->total_n; + size_t cut_num = dev_mem->num_cut; + dim3 DimBlock(score_kernel_config.long_blockdim, 1, 1); + dim3 longDimGrid(score_kernel_config.long_griddim, 1, 1); + dim3 shortDimGrid(score_kernel_config.short_griddim, 1, 1); + + // Run kernel + // printf("Grid Dim, %d\n", DimGrid.x); + score_generation_naive<<>>( + dev_mem->d_ax, dev_mem->d_ay, dev_mem->d_sid, dev_mem->d_range, dev_mem->d_cut, + dev_mem->d_f, dev_mem->d_p, total_n, cut_num); + cudaCheck(); +#ifdef DEBUG_VERBOSE + fprintf(stderr, "[M::%s] score generation kernel launch success\n", __func__); +#endif + + cudaCheck(); +} + diff --git a/gpu/plscore.cuh b/gpu/plscore.cuh new file mode 100644 index 00000000..56d57df0 --- /dev/null +++ b/gpu/plscore.cuh @@ -0,0 +1,24 @@ +#ifndef _PLSCORE_CUH_ +#define _PLSCORE_CUH_ + +#include "plmem.cuh" +#include "mmpriv.h" + +#ifdef __cplusplus +extern "C"{ +#endif + +#define MM_QSPAN 15 + +void plscore_upload_misc(Misc misc); +void plscore_async_naive_forward_dp(deviceMemPtr* dev_mem, cudaStream_t* stream); +void plscore_async_short_mid_forward_dp(deviceMemPtr* dev_mem,cudaStream_t* stream); +void plscore_async_long_forward_dp(deviceMemPtr* dev_mem,cudaStream_t* stream); + +extern score_kernel_config_t score_kernel_config; + +#ifdef __cplusplus +} +#endif + +#endif // _PLSCORE_CUH_ \ No newline at end of file diff --git a/gpu/plutils.h b/gpu/plutils.h new file mode 100644 index 00000000..a5cc6a8d --- /dev/null +++ b/gpu/plutils.h @@ -0,0 +1,144 @@ +#ifndef _PLUTILS_H_ +#define _PLUTILS_H_ + +#include +#include +#include +#include +#include +#include +#include + +#include "kalloc.h" +#include "minimap.h" + +/* Chaining Options */ + +/* structure for metadata and hits */ +// Sequence meta data +typedef struct { + long i; // read id + int seg_id; // seg id + char name[200]; // name of the sequence + uint32_t len; // name of the sequence + + // mi data + int n_alt; + int is_alt; // reference sequences only + + // sequence info + int qlen_sum; +} mm_seq_meta_t; + +typedef struct { + int max_iter, max_dist_x, max_dist_y, max_skip, bw, min_cnt, min_score, + is_cdna, n_seg; + float chn_pen_gap, chn_pen_skip; +} Misc; + +typedef struct { + mm_seq_meta_t *refs; + int n_refs; + Misc misc; +} input_meta_t; + +typedef struct { + mm_seq_meta_t seq; + + // minimap2 input data for reads + const char **qseqs; // sequences for each segment <- allocated in worker_for, freed in free_read after seeding + int *qlens; // query length for each segment <- allocated in worker_for, freed in free_read after seeding + int n_seg; // number of segs + +//DEBUG: for SCORE CHECK after chaining +#if defined(DEBUG_CHECK) && 0 + int32_t *f; + int64_t *p; +#endif // DEBUG_CHECK + int rep_len; + int frag_gap; + + // seeding outputs + uint64_t *mini_pos; // minimizer positions <- allocated in + int n_mini_pos; + + // seeding output, updated in chaining + mm128_t *a; // array of anchors + int64_t n; // number of anchors = n_a + + // chaining outputs + uint64_t *u; // scores for chains + int n_u; // number of chains formed from anchors == n_reg0 + +} chain_read_t; + +typedef struct seg_t { + size_t start_idx; + size_t end_idx; +//DEBUG: used for debug plchain_cal_long_seg_range_dis LONG_SEG_RANGE_DIS +#ifdef DEBUG_VERBOSE + size_t start_segid; + size_t end_segid; +#endif // DEBUG_VERBOSE +} seg_t; + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus +/* GPU chaining methods */ +// // backward, original chaining methods +// void chain_backword_cpu(const input_meta_t *meta, chain_read_t *read_arr, +// int n_read); +// // forward chaining methods +// void chain_forward_cpu(const input_meta_t *meta, chain_read_t *read_arr, +// int n_read); + +// gpu chaining methods +// initialization and cleanup +void init_stream_gpu(size_t *max_total_n, int *max_reads, + int *min_n, char gpu_config_file[], Misc misc); // for stream_gpu +void finish_stream_gpu(const mm_idx_t *mi, const mm_mapopt_t *opt, chain_read_t **batches, + int *num_reads, int num_batch, void *km); // for stream_gpu +void free_stream_gpu(int n_threads); // for stream_gpu free pinned memory +// chaining method +void chain_stream_gpu(const mm_idx_t *mi, const mm_mapopt_t *opt, chain_read_t **in_arr_ptr, int *n_read_ptr, int thread_id, void* km); + +/* Chaining backtracking methods */ +uint64_t *mg_chain_backtrack(void *km, int64_t n, const int32_t *f, + const int64_t *p, int32_t *v, int32_t *t, + int32_t min_cnt, int32_t min_sc, int32_t max_drop, + int32_t *n_u_, int32_t *n_v_); +mm128_t *compact_a(void *km, int32_t n_u, uint64_t *u, int32_t n_v, int32_t *v, mm128_t *a); + + +/* Post Chaining helpers */ +Misc build_misc(const mm_idx_t *mi, const mm_mapopt_t *opt, const int64_t qlen_sum, const int n_seg); +void post_chaining_helper(const mm_idx_t *mi, const mm_mapopt_t *opt, + chain_read_t *read, Misc misc, void *km); + +#ifdef __cplusplus +} +#endif // __cplusplus + +///////////////////////////////////////////////////// +/////////// Free Input Struct ///////////// +///////////////////////////////////////////////////// +// free input_iter pointers except a, because it is freed seperately. +static inline void free_read(chain_read_t *in, void* km) { + if (in->qseqs) kfree(km, in->qseqs); + if (in->qlens) kfree(km, in->qlens); + +//DEBUG: for SCORE CHECK after chaining +#if defined(DEBUG_CHECK) && 0 + if (in->f) kfree(km, in->f); + if (in->p) kfree(km, in->p); + in->f = 0, in->p = 0; +#endif + in->qseqs = 0, in->qlens = 0; + in->a = 0, in->u = 0; +} + +static inline void free_meta_struct(input_meta_t *meta, void *km) { + if (meta->refs) kfree(km, meta->refs); +} +#endif // _PLUTILS_H_ diff --git a/gpu_config.json b/gpu_config.json new file mode 100644 index 00000000..70cce79f --- /dev/null +++ b/gpu_config.json @@ -0,0 +1,25 @@ +{ + "num_streams": 1, + "min_n": 512, + "//min_n": "queries with less anchors will be handled on cpu", + "long_seg_buffer_size": 7888000, + "max_total_n": 4934400, + "max_read": 49344, + "avg_read_n": 20000, + "//avg_read_n": "expect average number of anchors per read", + "range_kernel": { + "blockdim": 512, + "cut_check_anchors": 10, + "//cut_check_anchors": "Number of anchors to check to attemp a cut", + "anchor_per_block": 32768, + "//anchor_per_block": "Number of anchors each block handle. Must be int * blockdim" + }, + "score_kernel": { + "short_blockdim": 64, + "long_blockdim": 64, + "mid_blockdim": 64, + "short_griddim": 16128, + "long_griddim": 2016, + "mid_griddim": 16128 + } +} \ No newline at end of file diff --git a/kalloc.c b/kalloc.c index 84995529..f379ff30 100644 --- a/kalloc.c +++ b/kalloc.c @@ -202,4 +202,6 @@ void km_stat(const void *_km, km_stat_t *s) s->capacity += size; s->largest = s->largest > size? s->largest : size; } + + s->meta_size = s->n_cores * sizeof(header_t); } diff --git a/kalloc.h b/kalloc.h index 93bff5e2..7ea6bae9 100644 --- a/kalloc.h +++ b/kalloc.h @@ -9,6 +9,7 @@ extern "C" { typedef struct { size_t capacity, available, n_blocks, n_cores, largest; + size_t meta_size; } km_stat_t; void *kmalloc(void *km, size_t size); diff --git a/kthread.c b/kthread.c index ffdf9408..8cf18d78 100644 --- a/kthread.c +++ b/kthread.c @@ -2,6 +2,7 @@ #include #include #include +#include #include "kthread.h" #if (defined(WIN32) || defined(_WIN32)) && defined(_MSC_VER) @@ -48,6 +49,10 @@ static void *ktf_worker(void *data) } while ((i = steal_work(w->t)) >= 0) w->t->func(w->t->data, i, w - w->t->w); +#if defined(__AMD_SPLIT_KERNELS__) + // call func one last time for this thread to signal end of all reads + w->t->func(w->t->data, -1, w - w->t->w); +#endif pthread_exit(0); } @@ -68,6 +73,11 @@ void kt_for(int n_threads, void (*func)(void*,long,int), void *data, long n) } else { long j; for (j = 0; j < n; ++j) func(data, j, 0); +#if defined(__AMD_SPLIT_KERNELS__) + // call once at the end to signal end of all reads + func(data, -1, 0); +#endif + } } diff --git a/lchain.c b/lchain.c index d0041578..f789d4d6 100644 --- a/lchain.c +++ b/lchain.c @@ -75,7 +75,7 @@ uint64_t *mg_chain_backtrack(void *km, int64_t n, const int32_t *f, const int64_ return u; } -static mm128_t *compact_a(void *km, int32_t n_u, uint64_t *u, int32_t n_v, int32_t *v, mm128_t *a) +mm128_t *compact_a(void *km, int32_t n_u, uint64_t *u, int32_t n_v, int32_t *v, mm128_t *a) { mm128_t *b, *w; uint64_t *u2; @@ -167,8 +167,8 @@ mm128_t *mg_lchain_dp(int max_dist_x, int max_dist_y, int bw, int max_skip, int // fill the score and backtrack arrays for (i = 0, max_ii = -1; i < n; ++i) { - int64_t max_j = -1, end_j; - int32_t max_f = a[i].y>>32&0xff, n_skip = 0; + int64_t max_j = -1, end_j; + int32_t max_f = a[i].y>>32&0xff, n_skip = 0; while (st < i && (a[i].x>>32 != a[st].x>>32 || a[i].x > a[st].x + max_dist_x)) ++st; if (i - st > max_iter) st = i - max_iter; for (j = i - 1; j >= st; --j) { diff --git a/main.c b/main.c index 0be99335..78d3ccee 100644 --- a/main.c +++ b/main.c @@ -7,7 +7,12 @@ #include "mmpriv.h" #include "ketopt.h" -#define MM_VERSION "2.24-r1122" +#if defined(__AMD_SPLIT_KERNELS__) + +#include "plutils.h" +#endif // (__AMD_SPLIT_KERNELS__) + +#define MM_VERSION "2.24-mm2-gb-biosys" #ifdef __linux__ #include @@ -86,6 +91,8 @@ static ko_longopt_t long_options[] = { { "mask-level", ko_required_argument, 'M' }, { "min-dp-score", ko_required_argument, 's' }, { "sam", ko_no_argument, 'a' }, + { "gpu-chain", ko_no_argument, 360 }, // use gpu for chaining + { "gpu-cfg", ko_required_argument, 361 }, { 0, 0, 0 } }; @@ -300,9 +307,17 @@ int main(int argc, char *argv[]) } else if (c == 'E') { opt.e = opt.e2 = strtol(o.arg, &s, 10); if (*s == ',') opt.e2 = strtol(s + 1, &s, 10); - } - } - if ((opt.flag & MM_F_SPLICE) && (opt.flag & MM_F_FRAG_MODE)) { + } else if (c == 360) { + opt.flag |= MM_F_GPU_CHAIN; // use gpu for chaining + } else if (c == 361) { + strcpy(opt.gpu_config_file, o.arg); + } + } + if (opt.flag & MM_F_SR) { + opt.max_chain_skip = INT32_MAX; + } + + if ((opt.flag & MM_F_SPLICE) && (opt.flag & MM_F_FRAG_MODE)) { fprintf(stderr, "[ERROR]\033[1;31m --splice and --frag should not be specified at the same time.\033[0m\n"); return 1; } @@ -356,7 +371,7 @@ int main(int argc, char *argv[]) fprintf(fp_help, " -Y use soft clipping for supplementary alignments\n"); fprintf(fp_help, " -t INT number of threads [%d]\n", n_threads); fprintf(fp_help, " -K NUM minibatch size for mapping [500M]\n"); -// fprintf(fp_help, " -v INT verbose level [%d]\n", mm_verbose); + fprintf(fp_help, " -v INT verbose level [%d]\n", mm_verbose); fprintf(fp_help, " --version show version number\n"); fprintf(fp_help, " Preset:\n"); fprintf(fp_help, " -x STR preset (always applied before other options; see minimap2.1 for details) []\n"); @@ -422,6 +437,16 @@ int main(int argc, char *argv[]) mm_idx_destroy(mi); continue; // no query files } +#if defined(__AMD_SPLIT_KERNELS__) + // initialize gpu + if (opt.flag & MM_F_GPU_CHAIN) { + // TODO: make misc different for each read + Misc misc = build_misc(mi, &opt, 0, 1); + init_stream_gpu(&opt.gpu_chain_max_anchors, + &opt.gpu_chain_max_reads, &opt.gpu_chain_min_n, + opt.gpu_config_file, misc); + } +#endif // (__AMD_SPLIT_KERNELS__) ret = 0; if (!(opt.flag & MM_F_FRAG_MODE)) { for (i = o.ind + 1; i < argc; ++i) { @@ -436,7 +461,10 @@ int main(int argc, char *argv[]) fprintf(stderr, "ERROR: failed to map the query file\n"); exit(EXIT_FAILURE); } - } +#if defined(__AMD_SPLIT_KERNELS__) + free_stream_gpu(n_threads); +#endif // (__AMD_SPLIT_KERNELS__) + } n_parts = idx_rdr->n_parts; mm_idx_reader_close(idx_rdr); @@ -453,7 +481,21 @@ int main(int argc, char *argv[]) fprintf(stderr, "[M::%s] CMD:", __func__); for (i = 0; i < argc; ++i) fprintf(stderr, " %s", argv[i]); - fprintf(stderr, "\n[M::%s] Real time: %.3f sec; CPU: %.3f sec; Peak RSS: %.3f GB\n", __func__, realtime() - mm_realtime0, cputime(), peakrss() / 1024.0 / 1024.0 / 1024.0); + // TODO: disabled because timer is not updated. + // fprintf(stderr, "\n[M::%s] Real time: %.3f sec; CPU: %.3f sec; Peak RSS: %.3f GB\n", __func__, realtime() - mm_realtime0, cputime(), peakrss() / 1024.0 / 1024.0 / 1024.0); + // fprintf(stderr, "----------------------------------------------------\n"); + // fprintf(stderr, " Sum (sec) Avg (sec) \n"); + // fprintf(stderr, "----------------------------------------------------\n"); + // fprintf(stderr, "Seed = %11.3f %11.3f\n", mm_time_seed_sum, + // mm_time_seed_sum / (double)n_threads); + // fprintf(stderr, "Chain = %11.3f %11.3f\n", mm_time_chain_sum, + // mm_time_chain_sum / (double)n_threads); + // fprintf(stderr, "Align = %11.3f %11.3f\n", mm_time_align_sum, + // mm_time_align_sum / (double)n_threads); + // fprintf(stderr, + // "----------------------------------------------------\n"); + // // fprintf(stderr, "Avg (seed + chain + align) per thread = %.3f secs\n", (mm_time_seed_sum + mm_time_chain_sum + mm_time_align_sum)/(double)n_threads); + // fprintf(stderr, "Total (seed + chain + align) for %d thread(s) = %.3f secs\n", n_threads, (mm_time_seed_sum + mm_time_chain_sum + mm_time_align_sum)); } return 0; } diff --git a/map.c b/map.c index 53114680..0cb285c1 100644 --- a/map.c +++ b/map.c @@ -12,8 +12,38 @@ struct mm_tbuf_s { void *km; - int rep_len, frag_gap; -}; + int rep_len, frag_gap; // updated per read. + double timers[MM_N_THR_TIMERS]; +}; // per thread + +#if defined(__AMD_SPLIT_KERNELS__) + +#include "plutils.h" + +#define N_ACCUM 64 + +typedef struct{ + int batchid; + void *km; // memory pool for each batch + int count; // number of reads in the batch + size_t total_n; // total number of anchors in the batch + chain_read_t *reads; +} mm_batch_trbuf_t; + +// local variables required for each read processed by a CPU thread +typedef struct { + mm_batch_trbuf_t acc_batch; + int is_full; + + mm_batch_trbuf_t launched_batch; + int has_launched; + + mm_batch_trbuf_t pending_batch; + int is_pending; + +} mm_trbuf_t; // per thread + +#endif mm_tbuf_t *mm_tbuf_init(void) { @@ -35,6 +65,98 @@ void *mm_tbuf_get_km(mm_tbuf_t *b) return b->km; } +#if defined(__AMD_SPLIT_KERNELS__) +void mm_trbuf_batch_init(mm_batch_trbuf_t *batch_, int batch_max_reads) { + batch_->count = 0; + batch_->total_n = 0; + batch_->reads = (chain_read_t *)malloc(sizeof(chain_read_t) * batch_max_reads); + memset(batch_->reads, 0, sizeof(chain_read_t) * batch_max_reads); + batch_->batchid = -1; + batch_->km = km_init(); +} + +void mm_trbuf_batch_reset(mm_batch_trbuf_t *batch_, int batch_max_reads, const mm_mapopt_t *opt) { + // free all the reads in the batch + for (int i = 0; i < batch_->count; i++) { + free_read(&batch_->reads[i], batch_->km); + } + + + + /* reset memory pool km */ + km_stat_t kmst; + if (batch_->km) { + chain_read_t *last_read = batch_->reads + batch_->count; + km_stat(batch_->km, &kmst); + if (mm_dbg_flag & MM_DBG_PRINT_QNAME) + fprintf(stderr, "QM\t%s\t%d\tBid=%d\tcap=%ld,avail=%ld,nCore=%ld,largest=%ld\n", + last_read->seq.name, last_read->seq.qlen_sum, batch_->batchid, kmst.capacity, kmst.available, kmst.n_cores, kmst.largest); + assert(kmst.n_blocks == kmst.n_cores); // otherwise, there is a memory leak + assert(kmst.capacity == kmst.meta_size + kmst.available); + if (kmst.largest > 1U<<28 || (opt->cap_kalloc > 0 && kmst.capacity > opt->cap_kalloc)) { + if (mm_dbg_flag & MM_DBG_PRINT_QNAME) + fprintf(stderr, "[W::%s] reset thread-local memory after read %s\n", __func__, last_read->seq.name); + km_destroy(batch_->km); + batch_->km = km_init(); + } + } + + batch_->count = 0; + batch_->total_n = 0; + batch_->batchid = -1; +} + +void mm_trbuf_batch_destroy(mm_batch_trbuf_t *batch_){ + // free reads in the batch + for (int i = 0; i < batch_->count; i++){ + free_read(&batch_->reads[i], batch_->km); + } + batch_->batchid = -1; + batch_->total_n = 0; + batch_->count = 0; + /* clean memory pool */ + km_stat_t kmst; + if (batch_->km) { + km_stat(batch_->km, &kmst); + if (mm_dbg_flag & MM_DBG_PRINT_QNAME) + fprintf(stderr, "Destroy memory pool cap=%ld,avail=%ld,nCore=%ld,largest=%ld\n", + kmst.capacity, kmst.available, kmst.n_cores, kmst.largest); + assert(kmst.n_blocks == kmst.n_cores); // otherwise, there is a memory leak + assert(kmst.capacity == kmst.meta_size + kmst.available); + km_destroy(batch_->km); + batch_->km = 0; + } + free(batch_->reads); + batch_->reads = 0; +} + + +mm_trbuf_t *mm_trbuf_init(const int batch_max_reads, const mm_mapopt_t *opt) +{ + mm_trbuf_t *tr; + tr = (mm_trbuf_t *)calloc(1, sizeof(mm_trbuf_t)); + tr->is_full = 0; + tr->is_pending = 0; + tr->has_launched = 0; + mm_trbuf_batch_init(&tr->acc_batch, batch_max_reads); + tr->acc_batch.batchid = 0; + mm_trbuf_batch_init(&tr->pending_batch, batch_max_reads); + tr->pending_batch.batchid = 1; + mm_trbuf_batch_init(&tr->launched_batch, batch_max_reads); + tr->launched_batch.batchid = 2; + return tr; +} + +void mm_trbuf_destroy(mm_trbuf_t *tr) +{ + if (tr == 0) return; + mm_trbuf_batch_destroy(&tr->acc_batch); + mm_trbuf_batch_destroy(&tr->pending_batch); + mm_trbuf_batch_destroy(&tr->launched_batch); + free(tr); +} +#endif + static int mm_dust_minier(void *km, int n, mm128_t *a, int l_seq, const char *seq, int sdust_thres) { int n_dreg, j, k, u = 0; @@ -229,6 +351,290 @@ static mm_reg1_t *align_regs(const mm_mapopt_t *opt, const mm_idx_t *mi, void *k return regs; } +#if defined(__AMD_SPLIT_KERNELS__) +void mm_map_seed(const mm_idx_t *mi, const mm_mapopt_t *opt, + chain_read_t *read_, mm_tbuf_t *b, void *km) { + int n_segs = read_->n_seg; + const int *qlens = read_->qlens; + const char **seqs = read_->qseqs; + const char *qname = read_->seq.name; + int *rep_len = &read_->rep_len; + int *qlen_sum = &read_->seq.qlen_sum; + int *n_mini_pos = &read_->n_mini_pos; + uint64_t **mini_pos = &read_->mini_pos; + int64_t *n_a = &read_->n; + mm128_t **a = &read_->a; + + int i; + mm128_v mv = {0,0,0}; + double *timers = b->timers; + double t1 = realtime(); + + for (i = 0, *qlen_sum = 0; i < n_segs; ++i) *qlen_sum += qlens[i]; + + if (*qlen_sum == 0 || n_segs <= 0 || n_segs > MM_MAX_SEG) return; + if (opt->max_qlen > 0 && *qlen_sum > opt->max_qlen) return; + + collect_minimizers(km, opt, mi, n_segs, qlens, seqs, &mv); + if (opt->q_occ_frac > 0.0f) mm_seed_mz_flt(km, &mv, opt->mid_occ, opt->q_occ_frac); + if (opt->flag & MM_F_HEAP_SORT) *a = collect_seed_hits_heap(km, opt, opt->mid_occ, mi, qname, &mv, *qlen_sum, n_a, rep_len, n_mini_pos, mini_pos); + else *a = collect_seed_hits(km, opt, opt->mid_occ, mi, qname, &mv, *qlen_sum, n_a, rep_len, n_mini_pos, mini_pos); + + if (mm_dbg_flag & MM_DBG_PRINT_SEED) { + fprintf(stderr, "RS\t%d\n", *rep_len); + for (i = 0; i < *n_a; ++i) + fprintf(stderr, "SD\t%s\t%d\t%c\t%d\t%d\t%d\n", mi->seq[(*a)[i].x<<1>>33].name, (int32_t)(*a)[i].x, "+-"[(*a)[i].x>>63], (int32_t)(*a)[i].y, (int32_t)((*a)[i].y>>32&0xff), + i == 0? 0 : ((int32_t)(*a)[i].y - (int32_t)(*a)[i-1].y) - ((int32_t)(*a)[i].x - (int32_t)(*a)[i-1].x)); + } + kfree(km, mv.a); + timers[MM_TIME_SEED] += realtime() - t1; +} + +Misc build_misc(const mm_idx_t *mi, const mm_mapopt_t *opt, const int64_t qlen_sum, const int n_seg) { + int max_chain_gap_qry, max_chain_gap_ref, is_splice = !!(opt->flag & MM_F_SPLICE), is_sr = !!(opt->flag & MM_F_SR); + float chn_pen_gap, chn_pen_skip; + + // set max chaining gap on the query and the reference sequence + if (is_sr) + max_chain_gap_qry = qlen_sum > opt->max_gap? qlen_sum : opt->max_gap; + else max_chain_gap_qry = opt->max_gap; + if (opt->max_gap_ref > 0) { + max_chain_gap_ref = opt->max_gap_ref; // always honor mm_mapopt_t::max_gap_ref if set + } else if (opt->max_frag_len > 0) { + max_chain_gap_ref = opt->max_frag_len - qlen_sum; + if (max_chain_gap_ref < opt->max_gap) max_chain_gap_ref = opt->max_gap; + } else max_chain_gap_ref = opt->max_gap; + + chn_pen_gap = opt->chain_gap_scale * 0.01 * mi->k; + chn_pen_skip = opt->chain_skip_scale * 0.01 * mi->k; + + Misc misc; + misc.max_iter = opt->max_chain_iter; // always set up MAX_UINT + misc.max_dist_y = max_chain_gap_qry; + misc.max_dist_x = max_chain_gap_ref; + misc.max_skip = opt->max_chain_skip; + misc.bw = opt->bw; + misc.min_cnt = opt->min_cnt; + misc.min_score = opt->min_chain_score; + misc.is_cdna = is_splice; + misc.n_seg = n_seg; + + misc.chn_pen_gap = chn_pen_gap; + misc.chn_pen_skip = chn_pen_skip; + + return misc; +} + +void post_chaining_helper(const mm_idx_t *mi, const mm_mapopt_t *opt, chain_read_t* read, Misc misc, void *km) { + int n_segs = read->n_seg; + const char *qname = read->seq.name; + int *rep_len = &read->rep_len; + int *frag_gap = &read->frag_gap; + int *qlen_sum = &read->seq.qlen_sum; + int *n_regs0 = &read->n_u; + int *n_mini_pos = &read->n_mini_pos; + uint64_t **mini_pos = &read->mini_pos; + int64_t *n_a = &read->n; + uint64_t **u = &read->u; + mm128_t **a = &read->a; + + int i; + mm128_v mv = {0, 0, 0}; + + if (opt->bw_long > opt->bw && + (opt->flag & (MM_F_SPLICE | MM_F_SR | MM_F_NO_LJOIN)) == 0 && + n_segs == 1 && *n_regs0 > 1) { // re-chain/long-join for long sequences + int32_t st = (int32_t)(*a)[0].y, en = (int32_t)(*a)[(int32_t)(*u)[0] - 1].y; + if (*qlen_sum - (en - st) > opt->rmq_rescue_size || en - st > *qlen_sum * opt->rmq_rescue_ratio) { + int32_t i; + for (i = 0, *n_a = 0; i < *n_regs0; ++i) *n_a += (int32_t)(*u)[i]; + kfree(km, *u); + radix_sort_128x(*a, (*a) + *n_a); + *a = mg_lchain_rmq(opt->max_gap, opt->rmq_inner_dist, opt->bw_long, opt->max_chain_skip, opt->rmq_size_cap, opt->min_cnt, opt->min_chain_score, + misc.chn_pen_gap, misc.chn_pen_skip, *n_a, *a, n_regs0, u, km); + } + } + else if (opt->max_occ > opt->mid_occ && *rep_len > 0 && + !(opt->flag & MM_F_RMQ)) { // re-chain, mostly for short reads + int rechain = 0; + if (*n_regs0 > 0) { // test if the best chain has all the segments + int n_chained_segs = 1, max = 0, max_i = -1, max_off = -1, off = 0; + for (i = 0; i < *n_regs0; ++i) { // find the best chain + if (max < (int)((*u)[i]>>32)) max = (*u)[i]>>32, max_i = i, max_off = off; + off += (uint32_t)(*u)[i]; + } + for (i = 1; i < (int32_t)(*u)[max_i]; ++i) // count the number of segments in the best chain + if (((*a)[max_off+i].y&MM_SEED_SEG_MASK) != ((*a)[max_off+i-1].y&MM_SEED_SEG_MASK)) + ++n_chained_segs; + if (n_chained_segs < n_segs) + rechain = 1; + } else rechain = 1; + if (rechain) { // redo chaining with a higher max_occ threshold + kfree(km, *a); + kfree(km, *u); + kfree(km, *mini_pos); + if (opt->flag & MM_F_HEAP_SORT) *a = collect_seed_hits_heap(km, opt, opt->max_occ, mi, qname, &mv, *qlen_sum, n_a, rep_len, n_mini_pos, mini_pos); + else *a = collect_seed_hits(km, opt, opt->max_occ, mi, qname, &mv, *qlen_sum, n_a, rep_len, n_mini_pos, mini_pos); + *a = mg_lchain_dp(misc.max_dist_x, misc.max_dist_y, opt->bw, opt->max_chain_skip, opt->max_chain_iter, opt->min_cnt, opt->min_chain_score, + misc.chn_pen_gap, misc.chn_pen_skip, misc.is_cdna, n_segs, *n_a, *a, n_regs0, u, km); + kfree(km, mv.a); + } + } + *frag_gap = misc.max_dist_x; +} + +void mm_map_chain(const mm_idx_t *mi, const mm_mapopt_t *opt, + chain_read_t *read_, mm_tbuf_t *b, void *km) { + int n_segs = read_->n_seg; + const char *qname = read_->seq.name; + int *rep_len = &read_->rep_len; + int *frag_gap = &read_->frag_gap; + int *qlen_sum = &read_->seq.qlen_sum; + int *n_regs0 = &read_->n_u; + int *n_mini_pos = &read_->n_mini_pos; + uint64_t **mini_pos = &read_->mini_pos; + int64_t *n_a = &read_->n; + uint64_t **u = &read_->u; + mm128_t **a = &read_->a; + + int i; + int max_chain_gap_qry, max_chain_gap_ref, is_splice = !!(opt->flag & MM_F_SPLICE), is_sr = !!(opt->flag & MM_F_SR); + mm128_v mv = {0,0,0}; + float chn_pen_gap, chn_pen_skip; + double *timers = b->timers; + + // set max chaining gap on the query and the reference sequence + if (is_sr) + max_chain_gap_qry = *qlen_sum > opt->max_gap? *qlen_sum : opt->max_gap; + else max_chain_gap_qry = opt->max_gap; + if (opt->max_gap_ref > 0) { + max_chain_gap_ref = opt->max_gap_ref; // always honor mm_mapopt_t::max_gap_ref if set + } else if (opt->max_frag_len > 0) { + max_chain_gap_ref = opt->max_frag_len - *qlen_sum; + if (max_chain_gap_ref < opt->max_gap) max_chain_gap_ref = opt->max_gap; + } else max_chain_gap_ref = opt->max_gap; + + chn_pen_gap = opt->chain_gap_scale * 0.01 * mi->k; + chn_pen_skip = opt->chain_skip_scale * 0.01 * mi->k; + if (opt->flag & MM_F_RMQ) { + *a = mg_lchain_rmq(opt->max_gap, opt->rmq_inner_dist, opt->bw, opt->max_chain_skip, opt->rmq_size_cap, opt->min_cnt, opt->min_chain_score, + chn_pen_gap, chn_pen_skip, *n_a, *a, n_regs0, u, km); + } else { + *a = mg_lchain_dp(max_chain_gap_ref, max_chain_gap_qry, opt->bw, opt->max_chain_skip, opt->max_chain_iter, opt->min_cnt, opt->min_chain_score, + chn_pen_gap, chn_pen_skip, is_splice, n_segs, *n_a, *a, n_regs0, u, km); + } + + if (opt->bw_long > opt->bw && (opt->flag & (MM_F_SPLICE|MM_F_SR|MM_F_NO_LJOIN)) == 0 && n_segs == 1 && *n_regs0 > 1) { // re-chain/long-join for long sequences + int32_t st = (int32_t)(*a)[0].y, en = (int32_t)(*a)[(int32_t)(*u)[0] - 1].y; + if (*qlen_sum - (en - st) > opt->rmq_rescue_size || en - st > *qlen_sum * opt->rmq_rescue_ratio) { + int32_t i; + for (i = 0, *n_a = 0; i < *n_regs0; ++i) *n_a += (int32_t)(*u)[i]; + kfree(km, *u); + radix_sort_128x(*a, (*a) + *n_a); + *a = mg_lchain_rmq(opt->max_gap, opt->rmq_inner_dist, opt->bw_long, opt->max_chain_skip, opt->rmq_size_cap, opt->min_cnt, opt->min_chain_score, + chn_pen_gap, chn_pen_skip, *n_a, *a, n_regs0, u, km); + } + } else if (opt->max_occ > opt->mid_occ && *rep_len > 0 && !(opt->flag & MM_F_RMQ)) { // re-chain, mostly for short reads + int rechain = 0; + if (*n_regs0 > 0) { // test if the best chain has all the segments + int n_chained_segs = 1, max = 0, max_i = -1, max_off = -1, off = 0; + for (i = 0; i < *n_regs0; ++i) { // find the best chain + if (max < (int)((*u)[i]>>32)) max = (*u)[i]>>32, max_i = i, max_off = off; + off += (uint32_t)(*u)[i]; + } + for (i = 1; i < (int32_t)(*u)[max_i]; ++i) // count the number of segments in the best chain + if (((*a)[max_off+i].y&MM_SEED_SEG_MASK) != ((*a)[max_off+i-1].y&MM_SEED_SEG_MASK)) + ++n_chained_segs; + if (n_chained_segs < n_segs) + rechain = 1; + } else rechain = 1; + if (rechain) { // redo chaining with a higher max_occ threshold + kfree(km, *a); + kfree(km, *u); + kfree(km, *mini_pos); + if (opt->flag & MM_F_HEAP_SORT) *a = collect_seed_hits_heap(km, opt, opt->max_occ, mi, qname, &mv, *qlen_sum, n_a, rep_len, n_mini_pos, mini_pos); + else *a = collect_seed_hits(km, opt, opt->max_occ, mi, qname, &mv, *qlen_sum, n_a, rep_len, n_mini_pos, mini_pos); + *a = mg_lchain_dp(max_chain_gap_ref, max_chain_gap_qry, opt->bw, opt->max_chain_skip, opt->max_chain_iter, opt->min_cnt, opt->min_chain_score, + chn_pen_gap, chn_pen_skip, is_splice, n_segs, *n_a, *a, n_regs0, u, km); + kfree(km, mv.a); + } + } + *frag_gap = max_chain_gap_ref; +} + +void mm_map_align(const mm_idx_t *mi, const mm_mapopt_t *opt, + chain_read_t *read_, mm_reg1_t **regs, int *n_regs, mm_tbuf_t *b, void *km) { + int n_segs = read_->n_seg; + const int *qlens = read_->qlens; + const char **seqs = read_->qseqs; + const char *qname = read_->seq.name; + int rep_len = read_->rep_len; + int frag_gap = read_->frag_gap; + int qlen_sum = read_->seq.qlen_sum; + int *n_regs0 = &read_->n_u; + int n_mini_pos = read_->n_mini_pos; + uint64_t **mini_pos = &read_->mini_pos; + uint64_t *u = read_->u; + mm128_t *a = read_->a; + + int i, j; + int max_chain_gap_ref = frag_gap; + int is_sr = !!(opt->flag & MM_F_SR); + uint32_t hash; + mm_reg1_t *regs0; + double *timers = b->timers; + double t1 = realtime(); + + for (i = 0; i < n_segs; ++i) n_regs[i] = 0, regs[i] = 0; // initialize regs + + hash = qname && !(opt->flag & MM_F_NO_HASH_NAME)? __ac_X31_hash_string(qname) : 0; + hash ^= __ac_Wang_hash(qlen_sum) + __ac_Wang_hash(opt->seed); + hash = __ac_Wang_hash(hash); + + regs0 = mm_gen_regs(km, hash, qlen_sum, *n_regs0, u, a, !!(opt->flag&MM_F_QSTRAND)); + if (mi->n_alt) { + mm_mark_alt(mi, *n_regs0, regs0); + mm_hit_sort(km, n_regs0, regs0, opt->alt_drop); // this step can be merged into mm_gen_regs(); will do if this shows up in profile + } + + if (mm_dbg_flag & (MM_DBG_PRINT_SEED|MM_DBG_PRINT_CHAIN)) + for (j = 0; j < *n_regs0; ++j) + for (i = regs0[j].as; i < regs0[j].as + regs0[j].cnt; ++i) + fprintf(stderr, "CN\t%d\t%s\t%d\t%c\t%d\t%d\t%d\n", j, mi->seq[a[i].x<<1>>33].name, (int32_t)a[i].x, "+-"[a[i].x>>63], (int32_t)a[i].y, (int32_t)(a[i].y>>32&0xff), + i == regs0[j].as? 0 : ((int32_t)a[i].y - (int32_t)a[i-1].y) - ((int32_t)a[i].x - (int32_t)a[i-1].x)); + + chain_post(opt, max_chain_gap_ref, mi, km, qlen_sum, n_segs, qlens, n_regs0, regs0, a); + if (!is_sr && !(opt->flag&MM_F_QSTRAND)) { + mm_est_err(mi, qlen_sum, *n_regs0, regs0, a, n_mini_pos, *mini_pos); + *n_regs0 = mm_filter_strand_retained(*n_regs0, regs0); + } + + if (n_segs == 1) { // uni-segment + regs0 = align_regs(opt, mi, km, qlens[0], seqs[0], n_regs0, regs0, a); + regs0 = (mm_reg1_t*)realloc(regs0, sizeof(*regs0) * *n_regs0); + mm_set_mapq(km, *n_regs0, regs0, opt->min_chain_score, opt->a, rep_len, is_sr); + n_regs[0] = *n_regs0, regs[0] = regs0; + } else { // multi-segment + mm_seg_t *seg; + seg = mm_seg_gen(km, hash, n_segs, qlens, *n_regs0, regs0, n_regs, regs, a); // split fragment chain to separate segment chains + free(regs0); + for (i = 0; i < n_segs; ++i) { + mm_set_parent(km, opt->mask_level, opt->mask_len, n_regs[i], regs[i], opt->a * 2 + opt->b, opt->flag&MM_F_HARD_MLEVEL, opt->alt_drop); // update mm_reg1_t::parent + regs[i] = align_regs(opt, mi, km, qlens[i], seqs[i], &n_regs[i], regs[i], seg[i].a); + mm_set_mapq(km, n_regs[i], regs[i], opt->min_chain_score, opt->a, rep_len, is_sr); + } + mm_seg_free(km, n_segs, seg); + if (n_segs == 2 && opt->pe_ori >= 0 && (opt->flag&MM_F_CIGAR)) + mm_pair(km, max_chain_gap_ref, opt->pe_bonus, opt->a * 2 + opt->b, opt->a, qlens, n_regs, regs); // pairing + } + timers[MM_TIME_ALIGN] += realtime() - t1; + + kfree(km, a); + kfree(km, u); + kfree(km, *mini_pos); +} +#endif + void mm_map_frag(const mm_idx_t *mi, int n_segs, const int *qlens, const char **seqs, int *n_regs, mm_reg1_t **regs, mm_tbuf_t *b, const mm_mapopt_t *opt, const char *qname) { int i, j, rep_len, qlen_sum, n_regs0, n_mini_pos; @@ -241,6 +647,8 @@ void mm_map_frag(const mm_idx_t *mi, int n_segs, const int *qlens, const char ** mm_reg1_t *regs0; km_stat_t kmst; float chn_pen_gap, chn_pen_skip; + double *timers = b->timers; + double t1 = realtime(); for (i = 0, qlen_sum = 0; i < n_segs; ++i) qlen_sum += qlens[i], n_regs[i] = 0, regs[i] = 0; @@ -263,7 +671,9 @@ void mm_map_frag(const mm_idx_t *mi, int n_segs, const int *qlens, const char ** fprintf(stderr, "SD\t%s\t%d\t%c\t%d\t%d\t%d\n", mi->seq[a[i].x<<1>>33].name, (int32_t)a[i].x, "+-"[a[i].x>>63], (int32_t)a[i].y, (int32_t)(a[i].y>>32&0xff), i == 0? 0 : ((int32_t)a[i].y - (int32_t)a[i-1].y) - ((int32_t)a[i].x - (int32_t)a[i-1].x)); } + timers[MM_TIME_SEED] += realtime() - t1; + t1 = realtime(); // set max chaining gap on the query and the reference sequence if (is_sr) max_chain_gap_qry = qlen_sum > opt->max_gap? qlen_sum : opt->max_gap; @@ -321,7 +731,9 @@ void mm_map_frag(const mm_idx_t *mi, int n_segs, const int *qlens, const char ** } b->frag_gap = max_chain_gap_ref; b->rep_len = rep_len; + timers[MM_TIME_CHAIN] += realtime() - t1; + t1 = realtime(); regs0 = mm_gen_regs(b->km, hash, qlen_sum, n_regs0, u, a, !!(opt->flag&MM_F_QSTRAND)); if (mi->n_alt) { mm_mark_alt(mi, n_regs0, regs0); @@ -358,6 +770,7 @@ void mm_map_frag(const mm_idx_t *mi, int n_segs, const int *qlens, const char ** if (n_segs == 2 && opt->pe_ori >= 0 && (opt->flag&MM_F_CIGAR)) mm_pair(b->km, max_chain_gap_ref, opt->pe_bonus, opt->a * 2 + opt->b, opt->a, qlens, n_regs, regs); // pairing } + timers[MM_TIME_ALIGN] += realtime() - t1; kfree(b->km, mv.a); kfree(b->km, a); @@ -409,8 +822,338 @@ typedef struct { int *n_reg, *seg_off, *n_seg, *rep_len, *frag_gap; mm_reg1_t **reg; mm_tbuf_t **buf; +#if defined(__AMD_SPLIT_KERNELS__) + mm_trbuf_t **trbuf; + int batch_max_reads; + size_t batch_max_anchors; + int gpu_min_n; +#endif } step_t; +#define MIN(a, b) ((a)<(b)?(a):(b)) +#define MAX(a, b) ((a)>(b)?(a):(b)) + +// consolidate timers from worker threads +void mm_consolidate_timers(step_t *s, pipeline_t *p) +{ + // TODO: disabled for sysbio submission + return; + mm_time_seed_min = 0; + mm_time_chain_min = 0; + mm_time_align_min = 0; + mm_time_seed_max = 0; + mm_time_chain_max = 0; + mm_time_align_max = 0; + mm_time_seed_avg = 0; + mm_time_chain_avg = 0; + mm_time_align_avg = 0; + for (int i = 0; i < p->n_threads; ++i) { + mm_time_seed_min = MIN(mm_time_seed_min, s->buf[i]->timers[MM_TIME_SEED]); + mm_time_chain_min = MIN(mm_time_chain_min, s->buf[i]->timers[MM_TIME_CHAIN]); + mm_time_align_min = MIN(mm_time_align_min, s->buf[i]->timers[MM_TIME_ALIGN]); + mm_time_seed_max = MAX(mm_time_seed_max, s->buf[i]->timers[MM_TIME_SEED]); + mm_time_chain_max = MAX(mm_time_chain_max, s->buf[i]->timers[MM_TIME_CHAIN]); + mm_time_align_max = MAX(mm_time_align_max, s->buf[i]->timers[MM_TIME_ALIGN]); + mm_time_seed_avg += s->buf[i]->timers[MM_TIME_SEED]; + mm_time_chain_avg += s->buf[i]->timers[MM_TIME_CHAIN]; + mm_time_align_avg += s->buf[i]->timers[MM_TIME_ALIGN]; + mm_time_seed_sum += s->buf[i]->timers[MM_TIME_SEED]; + mm_time_chain_sum += s->buf[i]->timers[MM_TIME_CHAIN]; + mm_time_align_sum += s->buf[i]->timers[MM_TIME_ALIGN]; + } + mm_time_seed_avg /= p->n_threads; + mm_time_chain_avg /= p->n_threads; + mm_time_align_avg /= p->n_threads; + + fprintf(stderr, "----------------------------------------------------\n"); + fprintf(stderr, " Min (sec) Max (sec) Avg (sec) \n"); + fprintf(stderr, "----------------------------------------------------\n"); + fprintf(stderr, "Seed = %11.3f %11.3f %11.3f\n", + mm_time_seed_min, mm_time_seed_max, mm_time_seed_avg); + fprintf(stderr, "Chain = %11.3f %11.3f %11.3f\n", + mm_time_chain_min, mm_time_chain_max, mm_time_chain_avg); + fprintf(stderr, "Align = %11.3f %11.3f %11.3f\n", + mm_time_align_min, mm_time_align_max, mm_time_align_avg); + fprintf(stderr, "----------------------------------------------------\n"); + fprintf(stderr, "Avg (seed + chain + align) per thread = %.3f secs\n", (mm_time_seed_avg + mm_time_chain_avg + mm_time_align_avg)); + fprintf(stderr, "Total (seed + chain + align) (all batches) for %d thread(s) = %.3f secs\n", p->n_threads, (mm_time_seed_sum + mm_time_chain_sum + mm_time_align_sum)); + +} + + +#if defined(__AMD_SPLIT_KERNELS__) + +void mm_trbuf_is_full(mm_trbuf_t* tr, step_t *s){ + while (tr->acc_batch.total_n > s->batch_max_anchors) { // if the batch is full + tr->is_full = 1; + tr->is_pending = 1; + // move last read from acc_batch to pending batch (another memory poll) + chain_read_t *read_ptr_acc_batch = &tr->acc_batch.reads[tr->acc_batch.count - 1]; + chain_read_t *read_ptr_pending_batch = &tr->pending_batch.reads[tr->pending_batch.count]; + /* deep copy, with memory pool transaction*/ + *read_ptr_pending_batch = *read_ptr_acc_batch; + if (s->p->opt->flag & MM_F_INDEPEND_SEG) { + read_ptr_pending_batch->qlens = (int *)kmalloc(tr->pending_batch.km, sizeof(int)); + read_ptr_pending_batch->qseqs = (const char **)kmalloc(tr->pending_batch.km, sizeof(const char*)); + read_ptr_pending_batch->qlens[0] = read_ptr_acc_batch->qlens[0]; + read_ptr_pending_batch->qseqs[0] = read_ptr_acc_batch->qseqs[0]; + } else { + read_ptr_pending_batch->qlens = (int *)kmalloc(tr->pending_batch.km, sizeof(int)*read_ptr_acc_batch->n_seg); + read_ptr_pending_batch->qseqs = (const char **)kmalloc(tr->pending_batch.km, sizeof(const char*)*read_ptr_acc_batch->n_seg); + memcpy(read_ptr_pending_batch->qlens, read_ptr_acc_batch->qlens, sizeof(int)*read_ptr_acc_batch->n_seg); + memcpy(read_ptr_pending_batch->qseqs, read_ptr_acc_batch->qseqs, sizeof(const char*)*read_ptr_acc_batch->n_seg); + } + read_ptr_pending_batch->mini_pos = (uint64_t*)kmalloc(tr->pending_batch.km, read_ptr_acc_batch->n_mini_pos * sizeof(uint64_t)); + read_ptr_pending_batch->a = (mm128_t*)kmalloc(tr->pending_batch.km, read_ptr_acc_batch->n * sizeof(mm128_t)); + memcpy(read_ptr_pending_batch->mini_pos, read_ptr_acc_batch->mini_pos, read_ptr_acc_batch->n_mini_pos * sizeof(uint64_t)); + memcpy(read_ptr_pending_batch->a, read_ptr_acc_batch->a, read_ptr_acc_batch->n * sizeof(mm128_t)); + strcpy(read_ptr_pending_batch->seq.name, read_ptr_acc_batch->seq.name); + tr->pending_batch.count++; + tr->pending_batch.total_n += read_ptr_acc_batch->n; + + // remove read from acc_batch + tr->acc_batch.count--; + tr->acc_batch.total_n -= read_ptr_acc_batch->n; + kfree(tr->acc_batch.km, read_ptr_acc_batch->mini_pos); + kfree(tr->acc_batch.km, read_ptr_acc_batch->a); + kfree(tr->acc_batch.km, read_ptr_acc_batch->qlens); + kfree(tr->acc_batch.km, read_ptr_acc_batch->qseqs); + } +} + +static void worker_for(void *_data, long i_in, int tid) // kt_for() callback +{ + step_t *s = (step_t *)_data; + long i = i_in; + int j, iread, off, pe_ori = s->p->opt->pe_ori; + double t = 0.0; + mm_tbuf_t *b = s->buf[tid]; + mm_trbuf_t *tr = s->trbuf[tid]; + + // Check if this is a valid read + if (i != -1) { + off = s->seg_off[i]; + + assert(s->n_seg[i] <= MM_MAX_SEG); + if (mm_dbg_flag & MM_DBG_PRINT_QNAME) { + fprintf(stderr, "QR\t%s\t%d\t%d\n", s->seq[off].name, tid, s->seq[off].l_seq); + t = realtime(); + } + + int n_indep_reads = (s->p->opt->flag & MM_F_INDEPEND_SEG) ? s->n_seg[i] : 1; + void *km; + chain_read_t *read_ptr = 0; + if ( n_indep_reads + tr->acc_batch.count <= s->batch_max_reads){ + read_ptr = tr->acc_batch.reads + tr->acc_batch.count; + tr->acc_batch.count += n_indep_reads; + km = tr->acc_batch.km; + } else { + read_ptr = tr->pending_batch.reads + tr->pending_batch.count; + tr->pending_batch.count += n_indep_reads; + tr->is_full = 1; + tr->is_pending = 1; + km = tr->pending_batch.km; + } + + if (s->p->opt->flag & MM_F_INDEPEND_SEG) { // assign to different chain_read_t if segments are indepent + for (j = 0; j < s->n_seg[i]; ++j) { + read_ptr->qlens = (int *)kmalloc(km, sizeof(int)); + read_ptr->qseqs = (const char **)kmalloc(km, sizeof(const char *)); + if (s->n_seg[i] == 2 && ((j == 0 && (pe_ori>>1&1)) || (j == 1 && (pe_ori&1)))) + mm_revcomp_bseq(&s->seq[off + j]); + read_ptr->qlens[0] = s->seq[off + j].l_seq; + read_ptr->qseqs[0] = s->seq[off + j].seq; + read_ptr->n_seg = 1; + + read_ptr->seq.i = i; + read_ptr->seq.seg_id = j; + strcpy(read_ptr->seq.name, s->seq[off + j].name); + read_ptr->seq.n_alt = s->p->mi->n_alt; + read_ptr->seq.is_alt = 0; + + read_ptr++; + } + + } else { + read_ptr->qlens = (int *)kmalloc(km, s->n_seg[i] * sizeof(int)); + read_ptr->qseqs = (const char **)kmalloc(km, s->n_seg[i] * sizeof(const char *)); + read_ptr->n_seg = s->n_seg[i]; + for (j = 0; j < s->n_seg[i]; ++j) { + if (s->n_seg[i] == 2 && ((j == 0 && (pe_ori>>1&1)) || (j == 1 && (pe_ori&1)))) + mm_revcomp_bseq(&s->seq[off + j]); + read_ptr->qlens[j] = s->seq[off + j].l_seq; + read_ptr->qseqs[j] = s->seq[off + j].seq; + } + + read_ptr->seq.i = i; + read_ptr->seq.seg_id = 0; + strcpy(read_ptr->seq.name, s->seq[off].name); + read_ptr->seq.n_alt = s->p->mi->n_alt; + read_ptr->seq.is_alt = 0; + + read_ptr++; + } + + + // Seed + for (j = 0; j < n_indep_reads; j++) { + read_ptr--; + mm_map_seed(s->p->mi, s->p->opt, read_ptr, b, km); + if + (tr->is_pending) tr->pending_batch.total_n += read_ptr->n; + else + tr->acc_batch.total_n += read_ptr->n; + assert(read_ptr->n_mini_pos >= 0); + } + + + // move reads from acc_batch to pending_batch if neccessary + mm_trbuf_is_full(tr, s); + } else { + tr->is_full = 1; // set acc_batch to ready to launch if this is the last read + } + + // Did we accumulate enough reads or get to the last batch of reads? + while (tr->is_full || (i_in == -1 && tr->has_launched)) { + // Chain + double t1 = realtime(); + /* perform chaining on acc_batch and move to launched_batch. move current pending batch to acc_batch. Store results in pending batch. */ + if (tr->is_full) { + + if (s->p->opt->flag & MM_F_GPU_CHAIN){ + // chaining on GPU + mm_batch_trbuf_t kernel_batch = tr->acc_batch; + chain_stream_gpu(s->p->mi, s->p->opt, &kernel_batch.reads, &kernel_batch.count, tid, tr->launched_batch.km); + // check if the returned batch exits, and/or is the launched_batch. + if (kernel_batch.reads) { // if chain_stream_gpu return non NULL reads + assert(tr->has_launched); + // FIXME: temporary solution for reads fail to fit in microbatch + // cpu kernel + for (kernel_batch.count; kernel_batch.countlaunched_batch.count; kernel_batch.count++) { + fprintf(stderr, "[WARNING] Run CPU kernel for read %d\n", kernel_batch.count); + mm_map_chain(s->p->mi, s->p->opt, &kernel_batch.reads[kernel_batch.count], b, tr->launched_batch.km); + } + assert(kernel_batch.count == tr->launched_batch.count); + kernel_batch = tr->launched_batch; + tr->launched_batch = tr->acc_batch; + tr->acc_batch = tr->pending_batch; + tr->pending_batch = kernel_batch; + tr->is_pending = 1; + } else { + assert(!tr->has_launched); // no launched batch + kernel_batch = tr->launched_batch; + tr->launched_batch = tr->acc_batch; + tr->acc_batch = tr->pending_batch; + tr->pending_batch = kernel_batch; + tr->is_pending = 0; + } + tr->is_full = 0; + tr->has_launched = 1; + } else { + // cpu kernel + for (iread=0; ireadacc_batch.count; iread++) { + mm_map_chain(s->p->mi, s->p->opt, &tr->acc_batch.reads[iread], b, tr->acc_batch.km); + } + mm_batch_trbuf_t kernel_batch = tr->acc_batch; + tr->acc_batch = tr->pending_batch; + tr->pending_batch = kernel_batch; + tr->has_launched = 0; + tr->is_full = 0; + tr->is_pending = 1; + // end of cpu kernel + } + + } else if (s->p->opt->flag & MM_F_GPU_CHAIN){ // clean up if all the pending kernels have been launched + assert(i_in == -1 && tr->has_launched); + mm_batch_trbuf_t kernel_batch; + finish_stream_gpu(s->p->mi, s->p->opt, &kernel_batch.reads, &kernel_batch.count, tid, tr->launched_batch.km); + // FIXME: temporary solution for reads fail to fit in microbatch + // cpu kernel + for (kernel_batch.count; kernel_batch.countlaunched_batch.count; kernel_batch.count++) { + fprintf(stderr, "[WARNING] Run CPU kernel for read %d\n", iread); + mm_map_chain(s->p->mi, s->p->opt, &kernel_batch.reads[kernel_batch.count], b, kernel_batch.km); + } + assert(kernel_batch.count == tr->launched_batch.count); + kernel_batch = tr->launched_batch; + tr->is_full = 0; + tr->is_pending = 1; + tr->has_launched = 0; + tr->launched_batch = tr->acc_batch; + tr->acc_batch = tr->pending_batch; + tr->pending_batch = kernel_batch; + } + + // NOTE: Because it just submit the GPU task, this timer doesn't make sense for GPU + b->timers[MM_TIME_CHAIN] += realtime() - t1; + + mm_batch_trbuf_t *batch = &tr->pending_batch; + + if (tr->is_pending){ + + /* Copy rep_len & frag_gap to step_t */ + for (iread = 0; iread < batch->count; iread++) { + i = batch->reads[iread].seq.i; + j = batch->reads[iread].seq.seg_id; + off = s->seg_off[i] + j; + for (int k = 0; k < batch->reads[iread].n_seg; k++) { + s->rep_len[off + k] = batch->reads[iread].rep_len; + s->frag_gap[off + k] = batch->reads[iread].frag_gap; + } + } + // Align + for (iread = 0; iread < batch->count; iread++) { + i = batch->reads[iread].seq.i; + off = s->seg_off[i]; + j = batch->reads[iread].seq.seg_id; + mm_map_align(s->p->mi, s->p->opt, &batch->reads[iread], &s->reg[off + j], &s->n_reg[off + j], b, batch->km) ; + if (s->p->opt->flag & MM_F_INDEPEND_SEG) { + if (s->n_seg[i] == 2 && ((j == 0 && (pe_ori >> 1 & 1)) || + (j == 1 && (pe_ori & 1)))) { + int k, t; + mm_revcomp_bseq(&s->seq[off + j]); + for (k = 0; k < s->n_reg[off + j]; ++k) { + mm_reg1_t *r = &s->reg[off + j][k]; + t = r->qs; + r->qs = batch->reads[iread].qlens[j] - r->qe; + r->qe = batch->reads[iread].qlens[j] - t; + r->rev = !r->rev; + } + } + } else { + for (j = 0; j < batch->reads[iread].n_seg; + ++j) { // flip the query strand and coordinate to the + // original read strand + if (s->n_seg[i] == 2 && + ((j == 0 && (pe_ori >> 1 & 1)) || + (j == 1 && (pe_ori & 1)))) { + int k, t; + mm_revcomp_bseq(&s->seq[off + j]); + for (k = 0; k < s->n_reg[off + j]; ++k) { + mm_reg1_t *r = &s->reg[off + j][k]; + t = r->qs; + r->qs = batch->reads[iread].qlens[j] - r->qe; + r->qe = batch->reads[iread].qlens[j] - t; + r->rev = !r->rev; + } + } + } + } + if (mm_dbg_flag & MM_DBG_PRINT_QNAME) + fprintf(stderr, "QT\t%s\t%d\t%.6f\n", s->seq[off].name, tid, realtime() - t); + } + + // reset pending batch + mm_trbuf_batch_reset(batch, s->batch_max_reads, s->p->opt); + tr->pending_batch = *batch; + tr->is_pending = 0; + tr->pending_batch.batchid = tr->acc_batch.batchid + 1; + + } // if tr->is_pending; + } +} +#endif + +#ifndef __AMD_SPLIT_KERNELS__ static void worker_for(void *_data, long i, int tid) // kt_for() callback { step_t *s = (step_t*)_data; @@ -457,6 +1200,7 @@ static void worker_for(void *_data, long i, int tid) // kt_for() callback if (mm_dbg_flag & MM_DBG_PRINT_QNAME) fprintf(stderr, "QT\t%s\t%d\t%.6f\n", s->seq[off].name, tid, realtime() - t); } +#endif static void merge_hits(step_t *s) { @@ -542,6 +1286,10 @@ static void *worker_pipeline(void *shared, int step, void *in) s->buf = (mm_tbuf_t**)calloc(p->n_threads, sizeof(mm_tbuf_t*)); for (i = 0; i < p->n_threads; ++i) s->buf[i] = mm_tbuf_init(); +#if defined(__AMD_SPLIT_KERNELS__) + s->trbuf = (mm_trbuf_t**)calloc(p->n_threads, sizeof(mm_trbuf_t*)); +#endif + s->n_reg = (int*)calloc(5 * s->n_seq, sizeof(int)); s->seg_off = s->n_reg + s->n_seq; // seg_off, n_seg, rep_len and frag_gap are allocated together with n_reg s->n_seg = s->seg_off + s->n_seq; @@ -557,15 +1305,36 @@ static void *worker_pipeline(void *shared, int step, void *in) return s; } else free(s); } else if (step == 1) { // step 1: map +#if defined(__AMD_SPLIT_KERNELS__) + step_t *s = (step_t *)in; + if (p->opt->flag & MM_F_GPU_CHAIN) { + s->batch_max_anchors = p->opt->gpu_chain_max_anchors; + // fprintf(stderr, "s->batch_max_anchors = %lu, p->opt->gpu_chain_max_anchors = %lu\n", s->batch_max_anchors, p->opt->gpu_chain_max_anchors); + s->batch_max_reads = p->opt->gpu_chain_max_reads; + s->gpu_min_n = p->opt->gpu_chain_min_n; + } else { + s->batch_max_anchors = SIZE_MAX; + s->batch_max_reads = N_ACCUM; + } + for (i = 0; i < p->n_threads; ++i) + s->trbuf[i] = mm_trbuf_init(s->batch_max_reads, p->opt); +#endif if (p->n_parts > 0) merge_hits((step_t*)in); else kt_for(p->n_threads, worker_for, in, ((step_t*)in)->n_frag); return in; } else if (step == 2) { // step 2: output void *km = 0; - step_t *s = (step_t*)in; + step_t *s = (step_t*)in; const mm_idx_t *mi = p->mi; + // consolidate timers from threads + mm_consolidate_timers (s, p); for (i = 0; i < p->n_threads; ++i) mm_tbuf_destroy(s->buf[i]); free(s->buf); +#if defined(__AMD_SPLIT_KERNELS__) + for (i = 0; i < p->n_threads; ++i) mm_trbuf_destroy(s->trbuf[i]); + free(s->trbuf); +#endif + if ((p->opt->flag & MM_F_OUT_CS) && !(mm_dbg_flag & MM_DBG_NO_KALLOC)) km = km_init(); for (k = 0; k < s->n_frag; ++k) { int seg_st = s->seg_off[k], seg_en = s->seg_off[k] + s->n_seg[k]; diff --git a/mi210_below50k.json b/mi210_below50k.json new file mode 100644 index 00000000..7fce048c --- /dev/null +++ b/mi210_below50k.json @@ -0,0 +1,29 @@ +{ + "//config is for": "aac cloud. Fits one batch + 5% x 4 long buffer avg_read_n 10k", + "num_streams": 1, + "min_n": 512, + "//min_n": "queries with less anchors will be handled on cpu", + "long_seg_buffer_size": 1117376000, + "max_total_n": 2036880000, + "max_read": 2036880, + "avg_read_n": 20000, + "//avg_read_n": "expect average number of anchors per read, not used if max_total_n and max_read are specified", + "range_kernel": { + "blockdim": 512, + "cut_check_anchors": 10, + "//cut_check_anchors": "Number of anchors to check to attemp a cut", + "anchor_per_block": 32768, + "//anchor_per_block": "Number of anchors each block handle. Must be int * blockdim" + }, + "score_kernel": { + "micro_batch": 6, + "mid_blockdim": 512, + "//static options for mid_blockdim": "128/256/512/1024", + "short_griddim": 3328, + "mid_griddim": 3328, + "long_griddim": 208, + "//normal reads benefit from more blocks": "long_griddim = 2 * num of CUs", + "long_seg_cutoff": 20, + "mid_seg_cutoff": 3 + } +} diff --git a/mi210_over50k.json b/mi210_over50k.json new file mode 100644 index 00000000..ec4dfdce --- /dev/null +++ b/mi210_over50k.json @@ -0,0 +1,28 @@ +{ + "//config is for": "aac cloud. Fits one batch + 5% x 4 long buffer avg_read_n 10k", + "num_streams": 1, + "min_n": 512, + "//min_n": "queries with less anchors will be handled on cpu", + "long_seg_buffer_size": 1117376000, + "max_total_n": 2036880000, + "max_read": 2036880, + "avg_read_n": 20000, + "//avg_read_n": "expect average number of anchors per read, not used if max_total_n and max_read are specified", + "range_kernel": { + "blockdim": 512, + "cut_check_anchors": 10, + "//cut_check_anchors": "Number of anchors to check to attemp a cut", + "anchor_per_block": 32768, + "//anchor_per_block": "Number of anchors each block handle. Must be int * blockdim" + }, + "score_kernel": { + "micro_batch": 6, + "mid_blockdim": 512, + "short_griddim": 3328, + "mid_griddim": 3328, + "long_griddim": 104, + "//long reads benefit from less blocks": "long_griddim = num of CUs", + "long_seg_cutoff": 20, + "mid_seg_cutoff": 3 + } +} diff --git a/minimap.h b/minimap.h index 13e12e03..def59118 100644 --- a/minimap.h +++ b/minimap.h @@ -40,6 +40,7 @@ #define MM_F_QSTRAND (0x100000000LL) #define MM_F_NO_INV (0x200000000LL) #define MM_F_NO_HASH_NAME (0x400000000LL) +#define MM_F_GPU_CHAIN (0x800000000LL) // use gpu for chaining #define MM_I_HPC 0x1 #define MM_I_NO_SEQ 0x2 @@ -61,6 +62,8 @@ #define MM_CIGAR_STR "MIDNSHP=XB" +#define MM_N_THR_TIMERS 8 + #ifdef __cplusplus extern "C" { #endif @@ -174,6 +177,12 @@ typedef struct { int64_t cap_kalloc; const char *split_prefix; + + /* gpu batch parameters */ + int gpu_chain_max_reads; + size_t gpu_chain_max_anchors; + int gpu_chain_min_n; + char gpu_config_file[1024]; } mm_mapopt_t; // index reader @@ -195,6 +204,25 @@ typedef struct mm_tbuf_s mm_tbuf_t; extern int mm_verbose, mm_dbg_flag; // verbose level: 0 for no info, 1 for error, 2 for warning, 3 for message (default); debugging flag extern double mm_realtime0; // wall-clock timer +typedef enum { + MM_TIME_SEED=0, + MM_TIME_CHAIN, + MM_TIME_ALIGN +} mm_thr_timer_t; + +extern double mm_time_seed_min; // timer for seeding (min for all threads) +extern double mm_time_seed_max; // timer for seeding (max for all threads) +extern double mm_time_seed_avg; // timer for seeding (avg of all threads) +extern double mm_time_seed_sum; // timer for seeding (sum of all threads) +extern double mm_time_chain_min; // timer for chaining (min for all threads) +extern double mm_time_chain_max; // timer for chaining (max for all threads) +extern double mm_time_chain_avg; // timer for chaining (avg of all threads) +extern double mm_time_chain_sum; // timer for chaining (sum of all threads) +extern double mm_time_align_min; // timer for alignment (min for all threads) +extern double mm_time_align_max; // timer for alignment (max for all threads) +extern double mm_time_align_avg; // timer for alignment (avg of all threads) +extern double mm_time_align_sum; // timer for alignment (sum of all threads) + /** * Set default or preset parameters * diff --git a/misc.c b/misc.c index f3a29f86..39b37a31 100644 --- a/misc.c +++ b/misc.c @@ -4,6 +4,18 @@ int mm_verbose = 1; int mm_dbg_flag = 0; double mm_realtime0; +double mm_time_seed_min = 0; +double mm_time_seed_max = 0; +double mm_time_seed_avg = 0; +double mm_time_seed_sum = 0; +double mm_time_chain_min = 0; +double mm_time_chain_max = 0; +double mm_time_chain_avg = 0; +double mm_time_chain_sum = 0; +double mm_time_align_min = 0; +double mm_time_align_max = 0; +double mm_time_align_avg = 0; +double mm_time_align_sum = 0; #if defined(WIN32) || defined(_WIN32) #include diff --git a/options.c b/options.c index 235b6dd8..858e7a50 100644 --- a/options.c +++ b/options.c @@ -61,6 +61,8 @@ void mm_mapopt_init(mm_mapopt_t *opt) opt->pe_ori = 0; // FF opt->pe_bonus = 33; + + strcpy(opt->gpu_config_file, "gpu_config.json"); } void mm_mapopt_update(mm_mapopt_t *opt, const mm_idx_t *mi) diff --git a/scripts/aac_integrated.sh b/scripts/aac_integrated.sh new file mode 100644 index 00000000..3db6304d --- /dev/null +++ b/scripts/aac_integrated.sh @@ -0,0 +1,38 @@ +#!/bin/bash +WORKSPACE_DIR=/shared/prod/home/liuxs/bioinfo/minimap2 +EXE_PATH=$WORKSPACE_DIR +CONFIG_PATH=$WORKSPACE_DIR +DATA_PATH=/shared/prod/home/liuxs/bioinfo/Profile_mm2/data +# NOTE: currently `-t n_thread`, the n_thread must be equal to the num_streams in gpu_config.json +N_THREAD=$(sed -n 's/.*"num_streams": \([0-9]*\).*/\1/p' ${CONFIG_PATH}/gpu_config.json) +# Array of LONG_BLOCK_SIZE values +MID_BLOCK_SIZES=( 64 ) +MID_CUTS=( 1 ) +LONG_CUTS=( 50 100 ) +GPU_CONFIGS=( gpu_config.json ) +DATA_SETS=( 1kto300k 200kto300k ) + +# Iterate over LONG_BLOCK_SIZES array +for DATA_SET in "${DATA_SETS[@]}" +do + QUERY_FILE=$DATA_PATH/random_500MBases_${DATA_SET}.fa + for MID_BLOCK_SIZE in "${MID_BLOCK_SIZES[@]}" + do + for MID_CUT in "${MID_CUTS[@]}" + do + for LONG_CUT in "${LONG_CUTS[@]}" + do + for GPU_CONFIG in "${GPU_CONFIGS[@]}" + do + echo "Executing with MID_BLOCK_SIZE=${MID_BLOCK_SIZE} MID_CUT=${MID_CUT} LONG_CUT=${LONG_CUT}" + + make clean + + make GPU_CONFIG=${GPU_CONFIG} SHORT_BLOCK_SIZE=64 MID_BLOCK_SIZE=${MID_BLOCK_SIZE} LONG_BLOCK_SIZE=1024 MID_CUT=${MID_CUT} LONG_CUT=${LONG_CUT} + + omniperf profile -n integrated_${MID_BLOCK_SIZE}_${LONG_CUT}_${DATA_SET}_report --device 0 -- ${EXE_PATH}/minimap2 ${DATA_PATH}/hg38.mmi -t ${N_THREAD} --max-chain-skip=2147483647 --gpu-chain ${QUERY_FILE} > test_logs.out + done + done + done + done +done \ No newline at end of file diff --git a/scripts/aac_omniperf.slurm b/scripts/aac_omniperf.slurm new file mode 100644 index 00000000..ecc55ef8 --- /dev/null +++ b/scripts/aac_omniperf.slurm @@ -0,0 +1,43 @@ +#!/bin/bash +#SBATCH --job-name=minimap2 # Job name +#SBATCH --partition=1CN128C8G2H_2IB_MI210_RHEL8 # Specify the MI210 GPU partition or queue +#SBATCH --gres=gpu:1 # Request 1 GPU +#SBATCH --nodes=1 # Number of nodes +#SBATCH --ntasks-per-node=1 # Number of tasks (processes) per node +#SBATCH --cpus-per-task=16 # Number of CPU cores per task +#SBATCH --mem=500g # Memory per node +#SBATCH --time=20:00:00 # Maximum execution time (HH:MM:SS) +#SBATCH --output=slurm_output/sample_sbatch_job.%j.out # Output file +#SBATCH --error=slurm_output/sample_sbatch_job.%j.err # Error file + +export MM2_ROOT=$HOME/minimap2 + +# Load necessary modules (if required) +source /etc/profile.d/modules.sh +scl enable gcc-toolset-11 bash +module unuse /shared/apps/modules/ubuntu/modulefiles +module use /shared/apps/modules/rhel8/modulefiles +module unuse /shared/apps/modules/rhel9/modulefiles +module unuse /shared/apps/modules/sles15sp4/modulefiles +module unuse /shared/apps/modules/centos8/modulefiles +module unuse /shared/apps/modules/rocky9/modulefiles + +module purge +module load rocm-5.7.1 +# module load rocm/6.0.0 +# export AMD_LOG_LEVEL=4 +# Replace the following line with the actual command(s) you want to run +cd $MM2_ROOT +make clean +export SUFFIX=-omniperf-1 +make MICRO_BATCH=1 GPU_CONFIG=aac_config_half.json SHORT_BLOCK_SIZE=64 LONG_BLOCK_SIZE=1024 MID_BLOCK_SIZE=512 MID_CUT=1 LONG_CUT=20 SUFFIX=$SUFFIX +# ./minimap2 -t 1 --max-chain-skip=2147483647 --gpu-chain /shareddata/umich_folder/data/ONT/hg38.mmi /shareddata/umich_folder/data/ONT/reads_4f452f4a-d82a-4580-981b-32d14b997217.fa +# ./minimap2 -t 1 --max-chain-skip=2147483647 --gpu-chain /shareddata/umich_folder/data/ONT/hg38.mmi /shareddata/umich_folder/data/ONT/random_500MBases_200kto300k.fa +omniperf profile -p omniperf_output -n long_seg_${SLURM_JOB_ID} --device 0 -- ./minimap2$SUFFIX -K 2000000000 -t 1 --max-chain-skip=2147483647 --gpu-chain /shareddata/umich_folder/data/ONT/hg38.mmi /shareddata/umich_folder/data/ONT/random_5GBases_90kto100k.fa +# omniperf profile -p omniperf_output -n long_seg_${SLURM_JOB_ID} --device 0 -- ./minimap2$SUFFIX -K 2000000000 -t 1 --max-chain-skip=2147483647 --gpu-chain /shareddata/umich_folder/data/ONT/hg38.mmi /shareddata/umich_folder/data/ONT/random_500MBases_90kto100k.fa +# omniperf profile -p omniperf_output -n long_seg_${SLURM_JOB_ID} --device 0 -- ./minimap2$SUFFIX -K 2000000000 -t 1 --max-chain-skip=2147483647 --gpu-chain /shareddata/umich_folder/data/ONT/hg38.mmi /shareddata/umich_folder/data/ONT/long_read_600M.fa +# omniperf profile -p omniperf_output n long_seg_${SLURM_JOB_ID} --device 0 -- ./minimap2 -K 2000000000 -t 1 --max-chain-skip=2147483647 --gpu-chain /shareddata/umich_folder/data/ONT/hg38.mmi /shareddata/umich_folder/data/ONT/reads_4f452f4a-d82a-4580-981b-32d14b997217.fa +# omniperf profile -p omniperf_output -n long_seg_${SLURM_JOB_ID} --device 0 -- ./minimap2 -K 2000000000 -t 1 --max-chain-skip=2147483647 --gpu-chain /shareddata/umich_folder/data/ONT/hg38.mmi /shareddata/umich_folder/data/ONT/random_500MBases_200kto300k.fa +# Optional: You can add post-processing commands here + +# End of the script diff --git a/scripts/aac_omnitrace.slurm b/scripts/aac_omnitrace.slurm new file mode 100644 index 00000000..f26f86fa --- /dev/null +++ b/scripts/aac_omnitrace.slurm @@ -0,0 +1,46 @@ +#!/bin/bash +#SBATCH --job-name=minimap2 # Job name +#SBATCH --partition=1CN128C8G2H_2IB_MI210_RHEL8 # Specify the MI210 GPU partition or queue +#SBATCH --gres=gpu:1 # Request 1 GPU +#SBATCH --nodes=1 # Number of nodes +#SBATCH --ntasks-per-node=1 # Number of tasks (processes) per node +#SBATCH --cpus-per-task=16 # Number of CPU cores per task +#SBATCH --mem=500g # Memory per node +#SBATCH --time=01:40:00 # Maximum execution time (HH:MM:SS) +#SBATCH --output=slurm_output/sample_sbatch_job.%j.out # Output file +#SBATCH --error=slurm_output/sample_sbatch_job.%j.err # Error file + +export MM2_ROOT=$HOME/minimap2 + +# Load necessary modules (if required) +source /etc/profile.d/modules.sh +scl enable gcc-toolset-11 bash +module unuse /shared/apps/modules/ubuntu/modulefiles +module use /shared/apps/modules/rhel8/modulefiles +module unuse /shared/apps/modules/rhel9/modulefiles +module unuse /shared/apps/modules/sles15sp4/modulefiles +module unuse /shared/apps/modules/centos8/modulefiles +module unuse /shared/apps/modules/rocky9/modulefiles + +module purge +module load rocm-5.7.1 +# module load rocm/6.0.0 +# export AMD_LOG_LEVEL=4 +# Replace the following line with the actual command(s) you want to run +cd $MM2_ROOT +make clean +make MICRO_BATCH=5 GPU_CONFIG=aac_config.json SHORT_BLOCK_SIZE=64 LONG_BLOCK_SIZE=1024 MID_BLOCK_SIZE=512 MID_CUT=1 LONG_CUT=100 DEBUG=1 DEBUG_ANALYSIS=1 +# omnitrace-sample -PTDH -E all -I rocm-smi -I roctracer -I rocprofiler -I roctx -o omni_output -- ./minimap2 -K 2500000000 -t 1 --max-chain-skip=2147483647 --gpu-chain /shareddata/umich_folder/data/ONT/hg38.mmi /shareddata/umich_folder/data/ONT/random_1GBases_100kto300k.fa +omnitrace-sample -PTDH -E all -I rocm-smi -I roctracer -I rocprofiler -I roctx -o omni_output -- ./minimap2 -K 2000000000 -t 1 --max-chain-skip=2147483647 --gpu-chain /shareddata/umich_folder/data/ONT/hg38.mmi /shareddata/umich_folder/data/ONT/random_5GBases_90kto100k.fa + +# ./minimap2 -t 1 --max-chain-skip=2147483647 --gpu-chain /shareddata/umich_folder/data/ONT/hg38.mmi /shareddata/umich_folder/data/ONT/reads_4f452f4a-d82a-4580-981b-32d14b997217.fa +# ./minimap2 -t 1 --max-chain-skip=2147483647 --gpu-chain /shareddata/umich_folder/data/ONT/hg38.mmi /shareddata/umich_folder/data/ONT/random_500MBases_200kto300k.fa +# rocprof --stats -o rocprof_output/long_seg.${SLURM_JOB_ID}.csv ./minimap2 -t 1 --max-chain-skip=2147483647 --gpu-chain /shareddata/umich_folder/data/ONT/hg38.mmi /shareddata/umich_folder/data/ONT/short_seg_reads_from_1kto10k_distri.fa +# rocprof --stats -o rocprof_output/long_seg.${SLURM_JOB_ID}.csv ./minimap2 -t 1 --max-chain-skip=2147483647 --gpu-chain /shareddata/umich_folder/data/ONT/hg38.mmi /shareddata/umich_folder/data/ONT/reads_4f452f4a-d82a-4580-981b-32d14b997217.fa +# omnitrace-sample -PTDH -E all -I rocm-smi -I roctracer -I rocprofiler -I roctx -o omni_output -- ./minimap2 -K 2000000000 -t 1 --max-chain-skip=2147483647 --gpu-chain /shareddata/umich_folder/data/ONT/hg38.mmi /shareddata/umich_folder/data/ONT/random_500MBases_200kto300k.fa +# omnitrace-sample -PTDH -E all -I rocm-smi -I roctracer -I rocprofiler -I roctx -o omni_output -- ./minimap2 -K 2000000000 -t 1 --max-chain-skip=2147483647 --gpu-chain /shareddata/umich_folder/data/ONT/hg38.mmi /shareddata/umich_folder/data/ONT/random_7GBases_10kto300k.fa +# Optional: You can add post-processing commands here + +echo "Exit: $?" + +# End of the script diff --git a/scripts/aac_rocprof.slurm b/scripts/aac_rocprof.slurm new file mode 100644 index 00000000..45839094 --- /dev/null +++ b/scripts/aac_rocprof.slurm @@ -0,0 +1,36 @@ +#!/bin/bash +#SBATCH --job-name=minimap2 # Job name +#SBATCH --partition=1CN128C8G2H_2IB_MI210_RHEL8 # Specify the MI210 GPU partition or queue +#SBATCH --gres=gpu:1 # Request 1 GPU +#SBATCH --nodes=1 # Number of nodes +#SBATCH --ntasks-per-node=1 # Number of tasks (processes) per node +#SBATCH --cpus-per-task=16 # Number of CPU cores per task +#SBATCH --mem=0 # Memory per node +#SBATCH --time=00:40:00 # Maximum execution time (HH:MM:SS) +#SBATCH --output=slurm_output/sample_sbatch_job.%j.out # Output file +#SBATCH --error=slurm_output/sample_sbatch_job.%j.err # Error file + +# Load necessary modules (if required) +source /etc/profile.d/modules.sh +scl enable gcc-toolset-11 bash +module unuse /shared/apps/modules/ubuntu/modulefiles +module use /shared/apps/modules/rhel8/modulefiles +module unuse /shared/apps/modules/rhel9/modulefiles +module unuse /shared/apps/modules/sles15sp4/modulefiles +module unuse /shared/apps/modules/centos8/modulefiles +module unuse /shared/apps/modules/rocky9/modulefiles + +module load rocm-5.4.3 +# export AMD_LOG_LEVEL=4 +# Replace the following line with the actual command(s) you want to run +cd /shared/prod/home/liuxs/bioinfo/minimap2/ +# make clean +# make MICRO_BATCH=4 GPU_CONFIG=gpu_config.json SHORT_BLOCK_SIZE=64 LONG_BLOCK_SIZE=1024 MID_BLOCK_SIZE=512 MID_CUT=1 LONG_CUT=100 +# ./minimap2 -t 1 --max-chain-skip=2147483647 --gpu-chain /shareddata/umich_folder/data/ONT/hg38.mmi /shareddata/umich_folder/data/ONT/reads_4f452f4a-d82a-4580-981b-32d14b997217.fa +# ./minimap2 -t 1 --max-chain-skip=2147483647 --gpu-chain /shareddata/umich_folder/data/ONT/hg38.mmi /shareddata/umich_folder/data/ONT/random_500MBases_200kto300k.fa +rocprof --hip-trace --roctx-trace rocprof_output/long_seg.${SLURM_JOB_ID}.csv ./minimap2 -K 2000000000 -t 1 --max-chain-skip=2147483647 --gpu-chain /shareddata/umich_folder/data/ONT/hg38.mmi /shareddata/umich_folder/data/ONT/random_2GBases_10kto300k.fa +# rocprof --stats -o rocprof_output/long_seg.${SLURM_JOB_ID}.csv ./minimap2 -t 1 --max-chain-skip=2147483647 --gpu-chain /shareddata/umich_folder/data/ONT/hg38.mmi /shareddata/umich_folder/data/ONT/reads_4f452f4a-d82a-4580-981b-32d14b997217.fa + +# Optional: You can add post-processing commands here + +# End of the script diff --git a/scripts/aac_timing_all.sh b/scripts/aac_timing_all.sh new file mode 100644 index 00000000..7ee82e52 --- /dev/null +++ b/scripts/aac_timing_all.sh @@ -0,0 +1,42 @@ +#!/bin/bash +WORKSPACE_DIR=/shared/prod/home/liuxs/bioinfo/minimap2 +EXE_PATH=$WORKSPACE_DIR +CONFIG_PATH=$WORKSPACE_DIR +# DATA_PATH=/shared/prod/home/liuxs/bioinfo/Profile_mm2/data +DATA_PATH=/shareddata/umich_folder/data/ONT +# NOTE: currently `-t n_thread`, the n_thread must be equal to the num_streams in gpu_config.json +N_THREAD=$(sed -n 's/.*"num_streams": \([0-9]*\).*/\1/p' ${CONFIG_PATH}/gpu_config.json) +# Array of LONG_BLOCK_SIZE values +MID_BLOCK_SIZES=( 512 ) +MID_CUTS=( 1 ) +LONG_CUTS=( 100 ) +GPU_CONFIGS=( gpu_config.json ) +# DATA_SETS=( 50kto100k ) +# DATA_SETS=( 1kto5k 1kto10k 1kto50k 1kto300k 200kto300k 1kto20k 1kto30k 1kto70k 1kto200k 10kto50k 10kto100k 50kto100k) +# DATA_SETS=( 1kto300k 50kto300k 100kto300k 150kto300k 200kto300k 250kto300k 1kto200k 20kto200k 50kto200k 70kto200k 100kto200k 130kto200k 150kto200k 170kto200k) +DATA_SETS=( 1kto5k 9kto10k 10kto20k 20kto30k 40kto50k 90kto100k 110kto120k 140kto150k 180kto200k 200kto250k 200kto300k ) + +# Iterate over LONG_BLOCK_SIZES array +for MID_BLOCK_SIZE in "${MID_BLOCK_SIZES[@]}" +do + for MID_CUT in "${MID_CUTS[@]}" + do + for LONG_CUT in "${LONG_CUTS[@]}" + do + echo "Executing with MID_BLOCK_SIZE=${MID_BLOCK_SIZE} MID_CUT=${MID_CUT} LONG_CUT=${LONG_CUT}" + make clean + make GPU_CONFIG=${GPU_CONFIG} SHORT_BLOCK_SIZE=64 MID_BLOCK_SIZE=${MID_BLOCK_SIZE} LONG_BLOCK_SIZE=1024 MID_CUT=${MID_CUT} LONG_CUT=${LONG_CUT} + + for GPU_CONFIG in "${GPU_CONFIGS[@]}" + do + for DATA_SET in "${DATA_SETS[@]}" + do + QUERY_FILE=$DATA_PATH/random_500MBases_${DATA_SET}.fa + echo "Executing on dataset ${DATA_SET}" + filename="profile_output/data-${DATA_SET}_profile_${N_THREAD}_midblk-${MID_BLOCK_SIZE}_cut-${LONG_CUT}" + ${EXE_PATH}/minimap2 ${DATA_PATH}/hg38.mmi -t ${N_THREAD} --max-chain-skip=2147483647 --gpu-chain ${QUERY_FILE} > test.out 2> $filename + done + done + done + done +done \ No newline at end of file diff --git a/scripts/acc_integrated.slurm b/scripts/acc_integrated.slurm new file mode 100755 index 00000000..cefcd2b5 --- /dev/null +++ b/scripts/acc_integrated.slurm @@ -0,0 +1,52 @@ +#!/bin/bash +#SBATCH --job-name=minimap2 # Job name +#SBATCH --partition=1CN128C8G2H_2IB_MI210_RHEL8 # Specify the MI210 GPU partition or queue +#SBATCH --gres=gpu:1 # Request 1 GPU +#SBATCH --nodes=1 # Number of nodes +#SBATCH --ntasks-per-node=1 # Number of tasks (processes) per node +#SBATCH --cpus-per-task=16 # Number of CPU cores per task +#SBATCH --mem=500g # Memory per node +#SBATCH --time=10:40:00 # Maximum execution time (HH:MM:SS) +#SBATCH --output=slurm_output/sample_sbatch_job.%j.out # Output file +#SBATCH --error=slurm_output/sample_sbatch_job.%j.err # Error file + +#salloc --partition=1CN128C8G2H_2IB_MI210_RHEL8 --gres=gpu:1 --nodes=1 --cpus-per-task=8 --time=01:00:00 + +export MM2_ROOT=$HOME/minimap2 + +# Load necessary modules (if required) +source /etc/profile.d/modules.sh +scl enable gcc-toolset-11 bash +module unuse /shared/apps/modules/ubuntu/modulefiles +module use /shared/apps/modules/rhel8/modulefiles +module unuse /shared/apps/modules/rhel9/modulefiles +module unuse /shared/apps/modules/sles15sp4/modulefiles +module unuse /shared/apps/modules/centos8/modulefiles +module unuse /shared/apps/modules/rocky9/modulefiles + +module purge +module load rocm-5.7.1 +# module load rocm-6.0.0 +# Replace the following line with the actual command(s) you want to run +cd $MM2_ROOT +# SOL exp + +# random data test +# export AMD_LOG_LEVEL=4 +#make clean +# debug level: N/A info analyze verbose +#make GPU=AMD DEBUG=analyze +./minimap2 -K 2000000000 -t 1 --gpu-chain --gpu-cfg /shared/prod/home/liuxs/bioinfo/minimap2/mi210_over50k.json /shareddata/umich_folder/data/ONT/hg38.mmi /shareddata/umich_folder/data/ONT/random_5GBases_90kto100k.fa > out.paf +echo "Exit: $?" +./minimap2 -K 2000000000 -t 1 --gpu-chain --gpu-cfg /shared/prod/home/liuxs/bioinfo/minimap2/mi210_below50k.json /shareddata/umich_folder/data/ONT/hg38.mmi /shareddata/umich_folder/data/ONT/random_5GBases_10kto20k.fa > out.paf +echo "Exit: $?" +./minimap2 -K 2000000000 -t 1 --gpu-chain --gpu-cfg /shared/prod/home/liuxs/bioinfo/minimap2/mi210_below50k.json /shareddata/umich_folder/data/ONT/hg38.mmi /shareddata/umich_folder/data/ONT/random_5GBases_1kto10k.fa > out.paf +echo "Exit: $?" +./minimap2 -K 2000000000 -t 1 --gpu-chain --gpu-cfg /shared/prod/home/liuxs/bioinfo/minimap2/mi210_below50k.json /shareddata/umich_folder/data/ONT/hg38.mmi /shareddata/umich_folder/data/ONT/random_5GBases_40kto50k.fa > out.paf +echo "Exit: $?" +./minimap2 -K 2000000000 -t 1 --gpu-chain --gpu-cfg /shared/prod/home/liuxs/bioinfo/minimap2/mi210_over50k.json /shareddata/umich_folder/data/ONT/hg38.mmi /shareddata/umich_folder/data/ONT/random_5GBases_100kto300k.fa > out.paf +echo "Exit: $?" + +# Optional: You can add post-processing commands here + +# End of the script diff --git a/scripts/amdxfx_integrated.sh b/scripts/amdxfx_integrated.sh new file mode 100755 index 00000000..670ef9a3 --- /dev/null +++ b/scripts/amdxfx_integrated.sh @@ -0,0 +1,3 @@ +#!/bin/bash +make GPU=AMD DEBUG=analyze +./minimap2 -t 1 --gpu-chain --gpu-cfg gfx1030.json data/hg38.mmi data/ONT/random_500MBases_90kto100k.fa > out.paf diff --git a/scripts/compare_mem.py b/scripts/compare_mem.py new file mode 100644 index 00000000..2212a113 --- /dev/null +++ b/scripts/compare_mem.py @@ -0,0 +1,118 @@ +import re +import argparse +import matplotlib.pyplot as plt + +# Define a function to extract segments from a line +def extract_segments(line): + # Define a regular expression pattern to match the numbers + pattern = r'total anchors: (\d+), total segs: (\d+),.*long: (\d+) : (\d+)' + + # Use re.search to find the match in the text + match = re.search(pattern, line) + + if match: + total_anchors = int(match.group(1)) + total_segs = int(match.group(2)) + long_segs = int(match.group(3)) + long_anchors = int(match.group(4)) + + # print("Total anchors:", total_anchors) + # print("Total segs:", total_segs) + # print("Long segs:", long_segs) + # print("Long anchors:", long_anchors) + else: + print("Pattern not found in the text.") + + return total_anchors, total_segs, long_segs, long_anchors + +def extract_dataset(line): + # Define a regular expression pattern to match the values + pattern = r'(\d+k)to(\d+k)' + + # Use re.search to find the match in the text + match = re.search(pattern, line) + + if match: + # Extract the matched values + start_value = match.group(1) + end_value = match.group(2) + + # print("Start value:", start_value) + # print("End value:", end_value) + else: + print("Pattern not found in the text.") + return start_value, end_value + +# Create an argument parser to get the output file name from the command line +parser = argparse.ArgumentParser(description='Compute and plot a histogram of segments from an output file.') +parser.add_argument('output_file', help='Path to the output file containing segment data') +# parser.add_argument('profile_file', help='Path to the profile file containing kernel runtime') +args = parser.parse_args() + +# Read the output file specified in the command line argument +tasks = {} +with open(args.output_file, 'r') as file: + batches = [] + for line in file: + if line.startswith("[M::main] CMD: "): + # finish last batch + start_value, end_value = extract_dataset(line) + print(f"Finish dataset {start_value} to {end_value}") + if len(batches) > 0: + batch_anchors = 0 + batch_long_anchors = 0 + batch_segs = 0 + batch_long_segs = 0 + for batch in batches: + batch_anchors += batch[0] + batch_segs += batch[1] + batch_long_segs += batch[2] + batch_long_anchors += batch[3] + print(f"Long anchor pct: {batch[3] / batch[0] * 100:.2f}%") + print(f"Long seg pct: {batch[2] / batch[1] * 100:.5f}%") + print(f"Batch long anchor pct: {batch_long_anchors / batch_anchors * 100:.2f}%") + print(f"Batch long seg pct: {batch_long_segs / batch_segs * 100:.5f}%") + tasks[(start_value, end_value)] = batches + if line.startswith("[M::main::"): + print("Start new dataset...") + batches = [] + if line.startswith("[DEBUG] total anchors"): + total_anchors, total_segs, long_segs, long_anchors = extract_segments(line) + batches.append((total_anchors, total_segs, long_segs, long_anchors)) + +# visualize the data +# plot the bar chart of the long anchor percentage and long segment percentage with respect to the dataset name +ax, fig = plt.subplots() +x = [] +y = [] +ticks = [] + +values = [] +for key, value in tasks.items(): + values.append((int(key[0][:-1]), f"{key[0]}to{key[1]}", value[0][3] / value[0][0] * 100)) +# sort ticks by the first element of the tuple +ticks.sort(key=lambda tup: tup[0]) +for value in values: + ticks.append(value[1]) + x.append(value[0]) + y.append(value[2]) + +plt.bar(x, y) +plt.xticks(x, ticks, rotation=90) +plt.xlabel("Dataset") +plt.ylabel("Long anchor percentage [%]") +plt.title("Long anchor percentage vs. dataset") +plt.tight_layout() +# save the figure to a file with the same name as the input file +output_filename = args.output_file.split('.')[0] +print(f"Saving figure to {output_filename}_long_anchor_percentage.png") +plt.savefig(f'{output_filename}_long_anchor_percentage.png') + + + +# # Save the figure with an appropriate name based on the input file name +# output_filename = args.output_file #.split('.')[0] # Remove the file extension +# plt.savefig(f'{output_filename}_segment_histogram.png') + +# Display the histogram +# plt.show() diff --git a/scripts/data_analysis.ipynb b/scripts/data_analysis.ipynb new file mode 100644 index 00000000..98df4441 --- /dev/null +++ b/scripts/data_analysis.ipynb @@ -0,0 +1,1001 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Requirement already satisfied: matplotlib in /shared/prod/home/joydong/minimap2/.conda/lib/python3.8/site-packages (3.7.5)\n", + "Requirement already satisfied: contourpy>=1.0.1 in /shared/prod/home/joydong/minimap2/.conda/lib/python3.8/site-packages (from matplotlib) (1.1.1)\n", + "Requirement already satisfied: cycler>=0.10 in /shared/prod/home/joydong/minimap2/.conda/lib/python3.8/site-packages (from matplotlib) (0.12.1)\n", + "Requirement already satisfied: fonttools>=4.22.0 in /shared/prod/home/joydong/minimap2/.conda/lib/python3.8/site-packages (from matplotlib) (4.49.0)\n", + "Requirement already satisfied: kiwisolver>=1.0.1 in /shared/prod/home/joydong/minimap2/.conda/lib/python3.8/site-packages (from matplotlib) (1.4.5)\n", + "Requirement already satisfied: numpy<2,>=1.20 in /shared/prod/home/joydong/minimap2/.conda/lib/python3.8/site-packages (from matplotlib) (1.24.4)\n", + "Requirement already satisfied: packaging>=20.0 in /shared/prod/home/joydong/minimap2/.conda/lib/python3.8/site-packages (from matplotlib) (23.2)\n", + "Requirement already satisfied: pillow>=6.2.0 in /shared/prod/home/joydong/minimap2/.conda/lib/python3.8/site-packages (from matplotlib) (10.2.0)\n", + "Requirement already satisfied: pyparsing>=2.3.1 in /shared/prod/home/joydong/minimap2/.conda/lib/python3.8/site-packages (from matplotlib) (3.1.1)\n", + "Requirement already satisfied: python-dateutil>=2.7 in /shared/prod/home/joydong/minimap2/.conda/lib/python3.8/site-packages (from matplotlib) (2.9.0)\n", + "Requirement already satisfied: importlib-resources>=3.2.0 in /shared/prod/home/joydong/minimap2/.conda/lib/python3.8/site-packages (from matplotlib) (6.1.2)\n", + "Requirement already satisfied: zipp>=3.1.0 in /shared/prod/home/joydong/minimap2/.conda/lib/python3.8/site-packages (from importlib-resources>=3.2.0->matplotlib) (3.17.0)\n", + "Requirement already satisfied: six>=1.5 in /shared/prod/home/joydong/minimap2/.conda/lib/python3.8/site-packages (from python-dateutil>=2.7->matplotlib) (1.16.0)\n" + ] + } + ], + "source": [ + "! pip install matplotlib" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [], + "source": [ + "oneK = 1024\n", + "oneM = 1024 * oneK\n", + "oneG = 1024 * oneM\n", + "from matplotlib import pyplot as plt" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "'total_anchors'\n", + "{'SOL': {}, '1kto5k': {'total_bases': 524288000, 'total_anchors': 2157742584, 'total_work': 17570719239, 'work_long': 5048145451, 'anchor_per_base': 4.1155673675537106, 'work_per_base': 33.51348731803894, 'avg_range': 8.143102596801695}, '10kto20k': {'total_bases': 524288000, 'total_anchors': 2372284362, 'total_work': 76583377636, 'work_long': 39767976508, 'anchor_per_base': 4.524773334503174, 'work_per_base': 146.071200630188, 'avg_range': 32.28254540759815}, '40kto50k': {'total_bases': 524288000, 'total_anchors': 2405098826, 'total_work': 165050618103, 'work_long': 131147302342, 'anchor_per_base': 4.587361957550049, 'work_per_base': 314.8090707836151, 'avg_range': 68.62529569211141}, '90kto100k': {'total_bases': 524288000, 'total_anchors': 2447974572, 'total_work': 257392983872, 'work_long': 222873697153, 'anchor_per_base': 4.669140953063965, 'work_per_base': 490.9381558837891, 'avg_range': 105.14528492904623}, '140kto150k': {'total_bases': 524288000, 'total_anchors': 2493238323, 'total_work': 343017810611, 'work_long': 311615099689, 'anchor_per_base': 4.75547470664978, 'work_per_base': 654.2545520992279, 'avg_range': 137.579230772557}}\n" + ] + } + ], + "source": [ + "dataset = {}\n", + "dataset_header = {\"total_bases\":0, \"total_anchors\":1, \"achors_long\":2, \"total_work\":3, \"work_long\":4, \"avg_range\":5, \"avg_range_long\":6, \"total_segs\":0, \"long_segs\":0, \"anchor_per_base\":7, \"work_per_base\":8}\n", + "\n", + "dataset[\"SOL\"] = {}\n", + "dataset[\"1kto5k\"] = { \n", + " \"total_bases\":500*oneM, \n", + " \"total_anchors\":2157742584, \n", + " \"total_work\":17570719239, \n", + " \"work_long\":5048145451\n", + "}\n", + "dataset[\"10kto20k\"] = { \n", + " \"total_bases\":500*oneM,\n", + " \"total_anchors\":2372284362,\n", + " \"total_work\":76583377636,\n", + " \"work_long\":39767976508\n", + "}\n", + "dataset[\"40kto50k\"] = {\n", + " \"total_bases\":500*oneM,\n", + " \"total_anchors\":2405098826,\n", + " \"total_work\":165050618103,\n", + " \"work_long\":131147302342,\n", + "} \n", + "dataset[\"90kto100k\"] = {\n", + " \"total_bases\":500*oneM,\n", + " \"total_anchors\":2447974572,\n", + " \"total_work\":257392983872,\n", + " \"work_long\":222873697153\n", + "}\n", + "dataset[\"140kto150k\"] = {\n", + " \"total_bases\":500*oneM,\n", + " \"total_anchors\":2493238323,\n", + " \"total_work\":343017810611,\n", + " \"work_long\":311615099689\n", + "}\n", + "\n", + "for key, item in dataset.items():\n", + " \n", + " try: \n", + " item[\"anchor_per_base\"] = item[\"total_anchors\"] / item[\"total_bases\"]\n", + " item[\"work_per_base\"] = item[\"total_work\"] / item[\"total_bases\"]\n", + " item[\"avg_range\"] = item[\"total_work\"] / item[\"total_anchors\"]\n", + " except Exception as e:\n", + " print(e)\n", + " \n", + "print(dataset) \n", + " " + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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imLSpEnUqVMHNzc3vv32WyZPnmzvw2Qy8fnnn/PXX3+xdOlSfvjhB4YNG8Ybb7zBX3/9ZT9vaGhosQtEAqWaRVEcFxcXPv30U2688UYee+wxAgICHGaRlOb80dHRtGvXjg8++IAhQ4bwwQcf4ObmxqBBg+xtPD09WbFiBb/88gvffPMN33//PZ988gnXXXcdP/744xX7v0pExJkUukWk0li4cCGhoaHMmDGjyH2LFi3iyy+/5K233irVH/INGzYE8qfDlod69ephs9nYvXs3zZo1sx8/fvw4Z86coV69emXqd/PmzezatYv33nuPIUOG2I8vW7bsb9X6888/k5aW5hCydu7cWeI+CkYy//jjD1atWuWwB3dERAT16tXjzz//5M8//6RNmzb2bb4KXoedO3favwaQv5f3/v377aPIJREcHMznn39O165duf766/njjz+IiIgo0WP37NmDYRgOb5Ls2rULyF8dGqBRo0akpaWVqqa/Ky8vD8C+8vfp06f5+eefef755/m///s/e7uCUevzhYeHM3r0aEaPHs2JEydo27Yt//vf/7jpppto1KgRkD+VvqzPqVu3brzyyiv89NNPBAcHExUVhclkonnz5vz+++/8/vvv9O3bt0x9n6+kb2CV1IIFCwDs09pL89peqJalS5eSnZ3NkiVLHGbDXGgqeKdOnejUqRP/+9//+PDDD/nnP//Jxx9/zIgRI2jUqBE//fQTXbp0ueT/ZWV9bTw8PFiyZAnXXnst999/PwEBAfbLLEpzfsgf7R4/fjzx8fF8+OGH9OnTx+GSHsifin799ddz/fXXM2nSJF588UWeffZZfvnllyv6cyUi4iyaXi4ilUJmZiaLFi2ib9++DBw4sMjH2LFjSU1NZcmSJaXqNyQkhO7duzNv3jwOHTrkcF9ZRsNvvvlmAKZMmeJwfNKkSQD06dOn1H3CuZHrwjUZhsHUqVPL1B/k15qXl+ewrZHVauXNN98scR8RERE0aNCAn3/+mXXr1tmv5y7QuXNnFi9ezM6dOx2mGvfs2RM3NzemTZvm8JzeeecdkpOTS/061a5dm59++onMzEx69epVZButCzl27Jh9aixASkoK77//Pq1bt7Zv2zVo0CBWrVrFDz/8UOTxZ86csQfk8rR06VIAYmJigOK//lD0+8xqtRaZDh0aGkpERIR9CnO7du1o1KgRr7/+erHbeZ08efKS9XXr1o3s7GymTJlC165d7eGvW7duLFiwgGPHjjlcz71371727t17yX4zMjLYsWOHw1Zz3t7eACXeyu5ili9fzsSJE2nQoAH//Oc/gZK/therpbg+kpOTmT9/vkO706dPFzlPwWyDgq/PoEGDsFqtTJw4scj58/LyHM7t7e1d5tfFz8+P77//nsjISAYPHmy/PKc05wcYPHgwJpOJcePGsW/fPoc95yF/6v75zn/OIiJVnUa6RaRSKFisp3///sXe36lTJ0JCQli4cCF33HFHqfqeNm0aXbt2pW3btjzwwAM0aNCAAwcO8M033xAbG1uqvmJiYrj33nuZM2cOZ86coUePHqxZs4b33nuPAQMG2BeAK62oqCgaNWrE448/ztGjR/Hz8+OLL75wuEa7tPr160eXLl3417/+xYEDB+x7VJf0GtYCXbt2tY8eFh7phvzQ/dFHH9nbFQgJCeHpp5/m+eef58Ybb6R///7s3LmTmTNn0qFDhyJ/uJdEZGQkP/74I9dccw29e/dm+fLl+Pn5XfQxTZo0Yfjw4axdu5aaNWsyb948jh8/7hCWnnjiCZYsWULfvn0ZOnQo7dq1Iz09nc2bN/P5559z4MABgoODS11vgaNHj9r3jM7JySEuLo7Zs2cTHBxsn1ru5+dH9+7defXVV8nNzaVWrVr8+OOP7N+/36Gv1NRUateuzcCBA4mJicHHx4effvqJtWvX8sYbbwD5o45z587lpptuonnz5tx3333UqlWLo0eP8ssvv+Dn52cP/Rdy9dVX4+Liws6dO3nggQfsx7t3725/E6dw6C6YLl9wnfyFrFmzhmuvvZYJEybw3HPPAfkjrwEBAbz11lv4+vri7e3NVVdddcnLCL777jt27NhBXl4ex48fZ/ny5Sxbtox69eqxZMkSPDw8SvXaQv4bFgDPPvssd955J66urvTr148bbrgBNzc3+vXrx8iRI0lLS+Ptt98mNDSU+Ph4++Pfe+89Zs6cya233kqjRo1ITU3l7bffxs/Pz/6GXY8ePRg5ciQvvfQSsbGx3HDDDbi6urJ7924+++wzpk6dysCBA+31zJo1i//+979ERkYSGhrKddddd9HXpbCQkBCWLVtGly5dGDBgAD///HOpzl/Qx4033shnn31GQEBAkTfMXnjhBVasWEGfPn2oV68eJ06cYObM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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Extracting the keys and corresponding values\n", + "keys = list(dataset.keys())[1:] # excluding 'SOL'\n", + "anchor_per_base_values = [dataset[key]['anchor_per_base'] for key in keys]\n", + "work_per_base_values = [dataset[key]['work_per_base'] for key in keys]\n", + "\n", + "# Plotting\n", + "fig, ax1 = plt.subplots(figsize=(10, 5))\n", + "\n", + "color = 'tab:blue'\n", + "ax1.set_xlabel('Dataset Keys')\n", + "ax1.set_ylabel('Anchor per Base', color=color)\n", + "ax1.plot(keys, anchor_per_base_values, marker='o', color=color, label='Anchor per Base')\n", + "ax1.tick_params(axis='y', labelcolor=color)\n", + "ax1.set_ylim(bottom=0) # setting lower limit to 0 for primary y-axis\n", + "\n", + "ax2 = ax1.twinx() \n", + "color = 'tab:green'\n", + "ax2.set_ylabel('Work per Base', color=color)\n", + "ax2.plot(keys, work_per_base_values, marker='s', color=color, label='Work per Base')\n", + "ax2.tick_params(axis='y', labelcolor=color)\n", + "ax2.set_ylim(bottom=0) # setting lower limit to 0 for secondary y-axis\n", + "\n", + "fig.tight_layout() \n", + "plt.title('Anchor and Work per Base w.r.t Dataset Keys')\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0: 1, \n", + "1: 0, \n" + ] + }, + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "with open (\"../data/read_4f452f4a-d82a-4580-981b-32d14b997217.long_seg.range\", \"r\") as f:\n", + " text = f.read()\n", + " text_s = text.split(\"#\")\n", + "\n", + " text_s.pop(0)\n", + " print(text_s[0])\n", + " print(text_s[1])\n", + " \n", + " range_list = []\n", + " for item in text_s:\n", + " item = item.strip('\\n')\n", + " stripped_number = item.split(':')[1].strip(', ')\n", + " range_list.append(int(stripped_number)) \n", + " plt.hist(range_list, bins=100)\n", + " plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Range Distribution" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "for i in [40, 50, 60, 70, 80, 90, 100]:\n", + " with open(\"../mid_range_dis_l%d.csv\" % i, \"r\") as file:\n", + " lines = file.readlines() # Read all lines into a list\n", + "\n", + " with open(\"../last_mid_range_dis_l%d.csv\" % i, \"w+\") as file:\n", + " file.write(lines[0])\n", + " file.write(lines[-1])" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[1647504372, 380090416, 92695059, 32601466, 16370200, 10385177, 7392392, 5679214, 4560064, 3785437]\n", + "[41909634, 11448864, 4485518, 2709970, 2070510, 1783081, 1575215, 1443760, 1338571, 1250311, 1174174, 1114907, 1051527, 1002328, 953713, 910297, 869125, 833108, 797669, 765226, 736393, 708454, 682753, 659905, 636737, 616486, 597261, 581347, 564090, 547151, 531283, 515540, 500663, 486728, 472705, 459859, 448150, 437260, 426333, 415405, 404493, 393476, 382675, 372297, 363457, 354355, 346335, 338912, 331773, 324710, 318087, 311742, 305397, 299543, 293507, 288783, 283586, 277722, 273054, 268531, 262763, 257072, 252209, 247583, 243414, 239682, 235681, 231623, 227933, 224192, 219739, 216698, 213165, 210636, 207412, 204392, 202120, 200002, 197568, 195719, 193888, 191925, 190921, 189496, 187216, 185677, 183492, 182352, 180826, 179255, 177594, 176383, 174684, 173635, 172765, 171128, 169884, 168875, 167752, 166342]\n", + "[401446, 115445, 67434, 51379, 44628, 41106, 38168, 36967, 36278, 35770, 35748, 35063, 34702, 34579, 34028, 34064, 34025, 34379, 34742, 35388, 36137, 36333, 36988, 37623, 38324, 38893, 39528, 39724, 40281, 40745, 41343, 41754, 42093, 42407, 42675, 43138, 43605, 43731, 44036, 44442, 44913, 45518, 46023, 46559, 47012, 47430, 47665, 48193, 48483, 48718, 49049, 49454, 49814, 50338, 50753, 50925, 51155, 51438, 51656, 51966, 52033, 51973, 52113, 52073, 51875, 51629, 51556, 51570, 51516, 51665, 51995, 51997, 52221, 52557, 52988, 53365, 53244, 53033, 53035, 53007, 52985, 53001, 53345, 53449, 53693, 53701, 53792, 53927, 54003, 53890, 53902, 53704, 53550, 53602, 53352, 53545, 53462, 53474, 53589, 53797]\n", + "Precent of Anchors > 16 bases: 0.07880844352464043\n", + "Precent of Anchors > 16 bases in short kernels 0.005714174534554116\n", + "Precent of Anchors > 64 bases in short kernels 0.0009200053704311544\n", + "Precent of Anchors > 16 bases in mid kernels 0.5466267298249484\n", + "Precent of Anchors > 64 bases in mid kernels 0.41671160802516294\n", + "Precent of Anchors in Long 0.03593610834850576\n", + "Precent of Anchors in Mid 0.0699026543868098\n" + ] + } + ], + "source": [ + "import csv\n", + "range_file = \"../data/random_500MBases_40kto50k.range_dis.csv\"\n", + "long_range_file = \"../data/random_500MBases_40kto50k.long_range_dis.csv\"\n", + "long_range_file = \"../data/random_500MBases_40kto50k.long_range_dis.csv\"\n", + "\n", + "# range_file = \"../range_dis_5.csv\"\n", + "# long_range_file = \"../long_range_dis_5.csv\"\n", + "# long_range_file = \"../last_long_range_dis_5.csv\"\n" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "short_kernel_it = []\n", + "avg_range_short = []\n", + "avg_range_mid = []\n", + "mid_anchors_total = []\n", + "mid_larger_than_64 = []\n", + "mid_larger_than_1024 = []\n", + "long_anchors_total = []\n", + "long_larger_than_64 = []\n", + "long_larger_than_1024 = []\n", + "short_anchors_total = []\n", + "short_larger_than_64 = []\n", + "short_larger_than_1024 = []\n", + "\n", + "def read_csv_files_range_dis(range_file, mid_range_file, long_range_file):\n", + " with open (range_file, newline='') as csvfile:\n", + " reader = csv.reader(csvfile, delimiter=',')\n", + " header = next(reader)\n", + " range_row = next(reader)\n", + " \n", + " # Convert numerical values to integers (excluding headers)\n", + " ranges_numeric = [int(x) for x in range_row[2:]]\n", + " # print(ranges_numeric[0:10])\n", + "\n", + " with open(mid_range_file, newline='') as csvfile:\n", + " reader = csv.reader(csvfile, delimiter=',')\n", + " header = next(reader)\n", + " mid_range_row = next(reader)\n", + " \n", + " # Convert numerical values to integers (excluding headers)\n", + " mid_ranges_numeric = [int(x) for x in mid_range_row[2:]]\n", + " # print(mid_ranges_numeric[0:100])\n", + "\n", + " with open(long_range_file, newline='') as csvfile:\n", + " reader = csv.reader(csvfile, delimiter=',')\n", + " header = next(reader)\n", + " long_range_row = next(reader)\n", + " \n", + " # Convert numerical values to integers (excluding headers)\n", + " long_ranges_numeric = [int(x) for x in long_range_row[2:]]\n", + " # print(long_ranges_numeric[0:100])\n", + "\n", + " short_ranges_numeric = [ x - y - z for x, y, z in zip(ranges_numeric, mid_ranges_numeric, long_ranges_numeric)]\n", + " \n", + " sum_short_kernel_it = 0\n", + " for i in range(0, len(short_ranges_numeric)):\n", + " sum_short_kernel_it += short_ranges_numeric[i]* (-(-i // 64))\n", + " \n", + " short_kernel_it.append(sum_short_kernel_it/sum(short_ranges_numeric))\n", + " # print(\"Precent of Anchors > 16 bases: \", sum(ranges_numeric[16:]) / sum(ranges_numeric))\n", + " # print(\"Precent of Anchors > 16 bases in short kernels\", sum(short_ranges_numeric[16:]) / sum(short_ranges_numeric))\n", + " # print(\"Precent of Anchors > 64 bases in short kernels\", sum(short_ranges_numeric[64:]) / sum(short_ranges_numeric))\n", + " # print(\"Precent of Anchors > 16 bases in mid kernels\", sum(mid_ranges_numeric[16:]) / sum(mid_ranges_numeric))\n", + " # print(\"Precent of Anchors > 64 bases in mid kernels\", sum(mid_ranges_numeric[64:]) / sum(mid_ranges_numeric))\n", + " # print(\"Precent of Anchors in Long \", sum(long_ranges_numeric) / sum(ranges_numeric))\n", + " # print(\"Precent of Anchors in Mid \", sum(mid_ranges_numeric) / sum(ranges_numeric))\n", + " \n", + " \n", + " \n", + " workload_range_dis = [value * index for index, value in enumerate(ranges_numeric)]\n", + " workload_long_range_dis = [value * index for index, value in enumerate(long_ranges_numeric)]\n", + " workload_mid_range_dis = [value * index for index, value in enumerate(mid_ranges_numeric)]\n", + " workload_short_range_dis = [x - y - z for x, y, z in zip(workload_range_dis, workload_mid_range_dis, workload_long_range_dis)]\n", + " \n", + " avg_range_short.append(sum(workload_short_range_dis) / sum(short_ranges_numeric))\n", + " avg_range_mid.append(sum(workload_mid_range_dis) / sum(mid_ranges_numeric))\n", + " \n", + " mid_anchors_total.append(sum(mid_ranges_numeric))\n", + " mid_larger_than_64.append(sum(mid_ranges_numeric[64:]))\n", + " mid_larger_than_1024.append(sum(mid_ranges_numeric[1024:]))\n", + " long_anchors_total.append(sum(long_ranges_numeric))\n", + " long_larger_than_64.append(sum(long_ranges_numeric[64:]))\n", + " long_larger_than_1024.append(sum(long_ranges_numeric[1024:]))\n", + " short_anchors_total.append(sum(short_ranges_numeric))\n", + " short_larger_than_64.append(sum(short_ranges_numeric[64:]))\n", + " short_larger_than_1024.append(sum(short_ranges_numeric[1024:]))" + ] + }, + { + "cell_type": "code", + "execution_count": 194, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Percentage of Anchors >64 0.06455149797657421\n" + ] + } + ], + "source": [ + "\n", + "MID_CUT_list = [1,2,3,4,5,6,7,8,9,10,12,15,18]\n", + "for i in MID_CUT_list:\n", + " range_file = \"../range_dis_%d.csv\" % i;\n", + " long_range_file = \"../long_range_dis_%d.csv\" % i;\n", + " mid_range_file = \"../last_mid_range_dis_%d.csv\" % i;\n", + " read_csv_files_range_dis(range_file, mid_range_file, long_range_file)\n", + " \n", + "# print(short_kernel_it)\n", + "# print(avg_range_mid)\n", + "# print(avg_range_short)\n", + "# print(mid_larger_than_1024)\n", + "# print(mid_larger_than_64)\n", + "print(\"Percentage of Anchors >64 \", sum(ranges_numeric[64:]) / sum(ranges_numeric))\n" + ] + }, + { + "cell_type": "code", + "execution_count": 185, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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3IkHDx5g8uTJJW63tbWtUA1EVLUx/BCRzg0ZMgQHDhzAjRs3cOPGDRw8eBBDhgyp8PEaN26s1viay5cvK/cnIv3B215EpHN169ZFjx49sGLFCgiCgB49esDe3r7CxxMEATKZTK39iEj/MPwQUZUwYsQIjB07FgCwaNGiFzrW+fPnyx0vBAAvvfQSAODChQto165dqftZW1sDAB4/flzsVlh6ejpsbGwqXiwRVTre9iKiKqFr167Iy8tDfn4+goODK3ycv/76C2fOnEHfvn3L3TcoKAj29vaYN29eidsLBzw3atQIBgYGOH78uMr2//77D48fP1aGKCKqHnjlh4iqBENDQ5w/f175vTpyc3ORkpICuVyO1NRUxMXFISoqCj179sSwYcPKfb+lpSWWLl2Kfv36ISQkBOPHj0fDhg3x4MEDbNy4ETdv3sT69etRq1YtvPvuu/joo49gZGSEFi1a4NatW/jkk0/w2muvoX379i907kRUuRh+iKjKKLy9pK64uDg4OTnByMgIdnZ28PHxwXfffYewsDAYGKh3YbtXr144dOgQoqKiMGjQIGRkZMDNzQ3+/v6YPXu2cr+FCxdizpw5+OSTT3Djxg04OjoiMDAQX375pVrji4io6pAJHPFHREREeoRjfoiIiEivMPwQUY31yy+/wMrKqsSvZs2a6bo8ItIR3vYiohorMzMTqampJW4zNjaGu7t7JVdERFUBww8RERHpFd72IiIiIr3C8ENERER6heGHiIiI9ArDDxEREekVhh8iIiLSKww/REREpFcYfoiIiEivMPwQERGRXvk/cmdXQBElRUkAAAAASUVORK5CYII=", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import matplotlib.pyplot as plt\n", + "\n", + "# Define x-axis values from 2 to 10\n", + "x_values = MID_CUT_list\n", + "\n", + "# Plotting\n", + "plt.plot(x_values, short_kernel_it, marker='o', color='blue')\n", + "plt.xlabel('MID_CUT')\n", + "plt.ylabel('Extra iterations in short kernel for range>64')\n", + "plt.title('Short Kernel Iteration Values')\n", + "plt.grid(True)\n", + "\n", + "# Set the lower limit of y-axis to 0\n", + "plt.ylim(0.2, max(short_kernel_it)*1.1)\n", + "\n", + "plt.show()\n", + "\n", + "\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 186, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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REREBoXJDRERERkUJjdERERkUJjcEBERkUFhckNEREQGhckNERERGRQmN0RERGRQmNwQERGRQWFyQ0RERAaFyQ0REREZFCY3REREZFCY3BAREZFBYXJDREREBoXJDRERERkUJjdERERkUIwLOgAiIiKdksk0q2duDmzdCtjaAsnJOdcVIu9xFVKymRqOlwQioGDHi8kNEVEhZ4h/fPRJNkOzeuZFgK0AbKcCyYqc6xruaBkmJjdEZLA0SQrMi5hja42tsP3eFsmKnH+9G3JCQGRImNwQUb7jnggi0icmN0SkQteJB5MOIspvvFqKiIiIDAr33BB9QLhXhYgod0xuiHSA55AQERUeTG6IiMiwzNDwh4F5OrD1ADAvHkiW51w3IO9hFVqajpcUBTxeTG7I4PFyYIl0vaHLaiOXXxtTTZbzIfyBM8A/PkT6xOSGCpTBnENSmP5Y53UZREQfOCY3lCWDOofEUH69ExGRRpjcUMHinggiItIxJjeUNR7jJyKiDxSTmw+Qpg+81ZQBP+yWiIg+QrxDMRERERkUJjdERERkUJjcEBERkUFhckNEREQGhckNERERGRQmN0RERGRQmNwQERGRQeF9bnSM96AhIiIqWNxzQ0RERAaFyQ0REREZFCY3REREZFCY3BAREZFBYXJDREREBoXJDRERERkUJjdERERkUJjcEBERkUFhckNEREQGhckNERERGRQmN0RERGRQmNwQERGRQWFyQ0RERAaFyQ0REREZFCY3REREZFCMNak0ceJEjRtcsmSJ1sEQERER5ZVGyc3ly5c1akwmk+UpGCIiIqK80ii5OXr0qL7jICIiItIJrc+5CQ8PR3BwMJKTkwEAQgjJbcybNw9169aFtbU1HBwc4OPjgzt37uQ6386dO1GpUiWYmZmhevXqOHDggORlExERkWGSnNzExsaidevWqFChAjp16oSoqCgAwLBhw+Dv7y+prePHj2P06NH4559/EBISgvT0dLRr1w6vX7/Odp6///4b/fv3x7Bhw3D58mX4+PjAx8cH169fl9oVIiIiMkCSk5sJEyZALpcjMjISFhYWyvK+ffvi4MGDkto6ePAgBg8ejKpVq8LT0xNBQUGIjIzExYsXs51n2bJl6NChAyZNmoTKlStj9uzZqF27Nn788UepXSEiIiIDpNE5N+86dOgQgoODUbp0aZVyDw8P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7twBAFSsWBH29va6jYyIiIhIC5Lvc5OUlIShQ4fCyckJzZo1Q7NmzeDk5IRhw4YhKSlJHzESERERaUxycjNhwgQcP34cv//+O+Li4hAXF4d9+/bh+PHj8Pf310eMRERERBqTfFjqt99+w65du9CiRQtlWadOnWBubo4+ffpg1apVuoyPiIiISBKtDkuVLFlSrdzBwYGHpYiIiKjASU5uGjZsiICAAKSkpCjLkpOTMXPmTD7AkoiIiAqc5MNSy5YtQ/v27VG6dGl4enoCAP7991+YmZkhODhY5wESERERSSE5ualWrRrCwsKwefNm3L59GwDQv39/DBw4EObm5joPkIiIiEgKyckNAFhYWGDEiBG6joWIiIgozySfc0NERERUmDG5ISIiIoPC5IaIiIgMCpMbIiIiMihaJTdxcXFYu3Ytpk6dihcvXgAALl26hCdPnug0OCIiIiKpJF8tdfXqVbRp0wa2trZ48OABRowYgeLFi2P37t2IjIzEhg0b9BEnERERkUYk77mZOHEiBg8ejLCwMJiZmSnLO3XqhBMnTug0OCIiIiKpJCc358+fx8iRI9XKnZ2dER0drZOgiIiIiLQlObkxNTVFQkKCWvndu3dhb2+vk6CIiIiItCU5uenatStmzZqF9PR0AIBMJkNkZCSmTJmCnj176jxAIiIiIikkJzeLFy9GYmIiHBwckJycjObNm6N8+fKwtrbGnDlz9BEjERERkcYkXy1la2uLkJAQnDp1ClevXkViYiJq166NNm3a6CM+IiIiIkm0enAmADRp0gRNmjTRZSxEREREeSY5uVm+fHmW5TKZDGZmZihfvjyaNWsGIyOjPAdHREREJJXk5Gbp0qV49uwZkpKSUKxYMQDAy5cvYWFhASsrK8TExKBs2bI4evQoXFxcdB4wERERUU4kn1A8d+5c1K1bF2FhYYiNjUVsbCzu3r2L+vXrY9myZYiMjISjoyMmTJigj3iJiIiIciR5z823336L3377DeXKlVOWlS9fHosWLULPnj1x//59LFiwgJeFExERUYGQvOcmKioKb968USt/8+aN8g7FTk5OePXqVd6jIyIiIpJIcnLTsmVLjBw5EpcvX1aWXb58GZ999hlatWoFALh27Rrc3d11FyURERGRhiQnN7/88guKFy8OLy8vmJqawtTUFHXq1EHx4sXxyy+/AACsrKyw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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Calculate the percentage of elements for each range\n", + "short_less_than_64 = [x - y for x, y in zip(short_anchors_total, short_larger_than_64)]\n", + "short_less_than_1024 = [x - y for x, y in zip(short_anchors_total, short_larger_than_1024)]\n", + "short_between_64_and_1024 = [x - y for x, y in zip(short_larger_than_64, short_larger_than_1024)]\n", + "\n", + "# Plotting\n", + "plt.bar(MID_CUT_list, short_less_than_64, color='b', label='<64')\n", + "plt.bar(MID_CUT_list, short_between_64_and_1024, bottom=short_less_than_64, color='g', label='64-1024')\n", + "plt.bar(MID_CUT_list, short_larger_than_1024, bottom=short_less_than_1024, color='r', label='>1024')\n", + "\n", + "plt.xlabel('MID_CUT_list')\n", + "plt.ylabel('Percentage of Anchors in Short Kernel')\n", + "plt.title('Percentage of Anchors in Different Ranges vs MID_CUT_list')\n", + "plt.legend()\n", + "plt.grid(True)\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": 187, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAjkAAAHWCAYAAACVEZinAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuNSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/xnp5ZAAAACXBIWXMAAA9hAAAPYQGoP6dpAAB1FElEQVR4nO3dd1xTV/8H8E+AsEQ2iCAKTsS9i9aNorWOxz2qCHW01TqwDmrL0LqtVftYrQOpWtyrrRMHOKtVsc46oVgVcBQQQYjk/v7wRx5jAuSGhBE+79eLl+bcc8/5njvClzslgiAIICIiIjIwRiUdABEREZE+MMkhIiIig8Qkh4iIiAwSkxwiIiIySExyiIiIyCAxySEiIiKDxCSHiIiIDBKTHCIiIjJITHKIiIjIIDHJoTLj4MGDaNy4MczNzSGRSJCamlrkNiUSCcLCworcjqEaOXIkPDw8SjSGyMhISCQSJCQkKJUvWrQI1atXh7GxMRo3bgwAeP36NaZNmwZ3d3cYGRmhT58+xR4vEb2hbt/t0KEDOnToUGwxlMskJ2/B5/2Ym5ujdu3aGD9+PJKTk0s6vCK7ceMGwsLCVH4plGXPnj3DwIEDYWFhgRUrVmDjxo2oUKGC2rrvrt93f37//fdijr54PHr0CGFhYbh8+XJJh5KvmJgYpXVhZmaGSpUqoUOHDpg7dy6ePHmiUTuHDx/GtGnT0KZNG6xfvx5z584FAERERGDRokXo378/fvrpJ0yePFmfwymS/fv3i0qwO3TooLTsLCws0LBhQyxduhRyuVx/gZYzb2+jmzZtUlunTZs2kEgkqF+/vlK5h4cHPvzwQ6Wyt9eZiYkJ7O3t0axZM0ycOBE3btwoUqy7d+9G9+7d4ejoCFNTU7i6umLgwIE4duyYok7e9+GFCxfUtvHhhx8q/pAZOXJkgd+deT8jR44sUtxinTlzBmFhYVr9YWui+3DKjlmzZsHT0xOvXr3CqVOnsHLlSuzfvx/Xrl2DpaVlSYentRs3biA8PBwdOnQo8b/CdeWPP/7AixcvMHv2bPj6+mo0T976fVfNmjV1HV6p8OjRI4SHh8PDw0NxZKOo1qxZo5dfoBMmTECLFi2Qm5uLJ0+e4MyZMwgNDcWSJUuwbds2dOrUSVF3+PDhGDx4MMzMzBRlx44dg5GREdatWwdTU1Olcjc3N3z33Xc6j1nX9u/fjxUrVohKdKpUqYJ58+YBAJ4+fYqoqChMnjwZT548wZw5c/QUaflkbm6OqKgofPTRR0rlCQkJOHPmDMzNzTVuq0uXLhgxYgQEQUBaWhr+/PNP/PTTT/jhhx+wYMECBAUFiYpNEAQEBgYiMjISTZo0QVBQEFxcXPD48WPs3r0bnTt3xunTp9G6dWtR7Y4dO1bp+zU+Ph4hISEYM2YM2rZtqyivUaOGqHbfdvjwYdHznDlzBuHh4Rg5ciRsbW1FzVuuk5zu3bujefPmAIBRo0bBwcEBS5Yswd69ezFkyJAitZ2ZmVmmE6XSJiUlBQBEbeBvr1/SjlQq1Uu7bdu2Rf/+/ZXK/vzzT3Tt2hX9+vXDjRs3ULlyZQCAsbExjI2NleqmpKTAwsJCKcHJKxf7JVgQQRDw6tUrWFhY6KzNorCxsVH6pfvJJ5/Ay8sL33//PWbNmqWynEh7H3zwAX755Rc8ffoUjo6OivKoqChUqlQJtWrVwr///qtRW7Vr11ZJlubPn4+ePXtiypQp8PLywgcffKBxbN9++y0iIyMxadIkLFmyBBKJRDFt5syZ2LhxI0xMxP969/HxgY+Pj+LzhQsXEBISAh8fH5X4tfXuPqtv5fJ0VX7y/nqMj49XlG3atAnNmjWDhYUF7O3tMXjwYDx48EBpvg4dOqB+/fq4ePEi2rVrB0tLS3z55ZcAgFevXiEsLAy1a9eGubk5KleujL59++LevXuK+eVyOZYuXYp69erB3NwclSpVwtixY1V2oLxDoadOnULLli1hbm6O6tWrY8OGDYo6kZGRGDBgAACgY8eOisOLMTExAIC9e/eiR48ecHV1hZmZGWrUqIHZs2cjNzdXZXmsWLEC1atXh4WFBVq2bImTJ0+qPZ+anZ2N0NBQ1KxZE2ZmZnB3d8e0adOQnZ2t0XLfvn27Yhk7Ojrio48+wsOHD5WWr7+/PwCgRYsWej9c+vDhQwQGBqJSpUowMzNDvXr1EBERoVQn75D2tm3bEB4eDjc3N1SsWBH9+/dHWloasrOzMWnSJDg7O8PKygoBAQFql4eY7evGjRvo2LEjLC0t4ebmhoULFyrF06JFCwBAQECAYr1HRkYCAO7cuYN+/frBxcUF5ubmqFKlCgYPHoy0tLQCl8W71+QkJCRAIpFg8eLFWL16NWrUqAEzMzO0aNECf/zxh5jFrKJRo0ZYunQpUlNT8d///ldR/u55fYlEgvXr1+Ply5dK45RIJDh+/DiuX7+ust2L3ccOHTqE5s2bw8LCAj/++CMAIDU1FZMmTYK7uzvMzMxQs2ZNLFiwQOlIl6bLZ+TIkVixYoViPHk/Ypmbm6NFixZ48eKF4g8BALhy5QpGjhyJ6tWrw9zcHC4uLggMDMSzZ8+U5g8LC4NEIsHdu3cVfyXb2NggICAAmZmZSnWzsrIwYcIEODo6omLFiujVqxcePnyo9ro2TfYhAPj+++9Rr149WFpaws7ODs2bN0dUVFS+401OToaJiQnCw8NVpt26dQsSiUSx7chkMoSHh6NWrVowNzeHg4MD3n//fURHRxe6XAGgd+/eMDMzw/bt25XKo6KiMHDgwCInlA4ODtiyZQtMTExEHYXLysrCvHnz4OXlhcWLF6vdboYPH46WLVsWKT59Ufc7pKDtICwsDFOnTgUAeHp6KvYVTS/HKNdHct6Vl3g4ODgAAObMmYOvv/4aAwcOxKhRo/DkyRN8//33aNeuHeLi4pT+Ynz27Bm6d++OwYMH46OPPkKlSpWQm5uLDz/8EEePHsXgwYMxceJEvHjxAtHR0bh27ZrikN/YsWMRGRmJgIAATJgwAfHx8fjvf/+LuLg4nD59Wumv6bt376J///74+OOP4e/vj4iICIwcORLNmjVDvXr10K5dO0yYMAHLly/Hl19+ibp16wKA4t/IyEhYWVkhKCgIVlZWOHbsGEJCQpCeno5FixYp+lm5ciXGjx+Ptm3bYvLkyUhISECfPn1gZ2eHKlWqKOrJ5XL06tULp06dwpgxY1C3bl1cvXoV3333HW7fvo09e/YUuMzzxt2iRQvMmzcPycnJWLZsGU6fPq1YxjNnzkSdOnWwevVqxSkoTQ6XpqWl4enTp0plEolEsX7VSU5OxnvvvQeJRILx48fDyckJBw4cwMcff4z09HRMmjRJqf68efNgYWGBGTNm4O7du/j+++8hlUphZGSEf//9F2FhYfj9998RGRkJT09PhISEKOYVs339+++/6NatG/r27YuBAwdix44dmD59Oho0aIDu3bujbt26mDVrlsqh5datWyMnJwd+fn7Izs7G559/DhcXFzx8+BC//fYbUlNTYWNjU+iyfFdUVBRevHiBsWPHQiKRYOHChejbty/u379fpKM/edv24cOH8/3i37hxI1avXo3z589j7dq1AIAmTZpg48aNmDNnDjIyMhSndPK2ezH72K1btzBkyBCMHTsWo0ePRp06dZCZmYn27dvj4cOHGDt2LKpWrYozZ84gODgYjx8/xtKlS0Utn7Fjx+LRo0eIjo7Gxo0btV5ewP8Sq7e3l+joaNy/fx8BAQFwcXHB9evXsXr1aly/fh2///67yi/GgQMHwtPTE/PmzcOlS5ewdu1aODs7Y8GCBYo6I0eOxLZt2zB8+HC89957iI2NRY8ePVTi0XQfWrNmDSZMmID+/ftj4sSJePXqFa5cuYJz585h6NChasdaqVIltG/fHtu2bUNoaKjStK1bt8LY2FjxR15YWBjmzZuHUaNGoWXLlkhPT8eFCxdw6dIldOnSpdDlamlpid69e2Pz5s349NNPAbw52nj9+nWsXbsWV65cKbSNwlStWhXt27fH8ePHkZ6eDmtr60LnOXXqFJ4/f45JkyYZxJG7wraDvn374vbt29i8eTO+++47xVE1JycnzToQyqH169cLAIQjR44IT548ER48eCBs2bJFcHBwECwsLIR//vlHSEhIEIyNjYU5c+YozXv16lXBxMREqbx9+/YCAGHVqlVKdSMiIgQAwpIlS1RikMvlgiAIwsmTJwUAws8//6w0/eDBgyrl1apVEwAIJ06cUJSlpKQIZmZmwpQpUxRl27dvFwAIx48fV+k3MzNTpWzs2LGCpaWl8OrVK0EQBCE7O1twcHAQWrRoIchkMkW9yMhIAYDQvn17RdnGjRsFIyMj4eTJk0ptrlq1SgAgnD59WqW/PDk5OYKzs7NQv359ISsrS1H+22+/CQCEkJAQRVneOvvjjz/ybe/duup+zMzMlOoCEEJDQxWfP/74Y6Fy5crC06dPleoNHjxYsLGxUSy/48ePCwCE+vXrCzk5OYp6Q4YMESQSidC9e3el+X18fIRq1aopPmuzfW3YsEFRlp2dLbi4uAj9+vVTlP3xxx8CAGH9+vVKbcbFxQkAhO3btxew1NTz9/dXijs+Pl4AIDg4OAjPnz9XlO/du1cAIPz6668Ftpe33AqKpVGjRoKdnZ3ic976jI+PV4qrQoUKKvO2b99eqFevnlKZNvvYwYMHlerOnj1bqFChgnD79m2l8hkzZgjGxsZCYmKiIAjils+4ceMEMV/B7du3F7y8vIQnT54IT548Ef766y9h6tSpAgChR48eSnXV7eebN29W+f4IDQ0VAAiBgYFKdf/zn/8IDg4Ois8XL14UAAiTJk1Sqjdy5Eit96HevXurrCtN/PjjjwIA4erVq0rl3t7eQqdOnRSfGzVqpLJcNPH2Nvrbb78JEolEsX6nTp0qVK9eXRAE9dtatWrVVPoEIIwbNy7f/iZOnCgAEP7880+N4lu2bJkAQNi9e7dG9Qv77uzRo4fSPv62/L5TNKVu323fvr3S7xBNtoNFixaptKOpcn26ytfXF05OTnB3d8fgwYNhZWWF3bt3w83NDbt27YJcLsfAgQPx9OlTxY+Liwtq1aqF48ePK7VlZmaGgIAApbKdO3fC0dERn3/+uUrfeX9Jbd++HTY2NujSpYtSP82aNYOVlZVKP97e3koXgDk5OaFOnTq4f/++RmN++9qCFy9e4OnTp2jbti0yMzPx119/AXhzHvbZs2cYPXq00nndYcOGwc7OTqm97du3o27duvDy8lKKP+/U37vxv+3ChQtISUnBZ599pnQRX48ePeDl5YV9+/ZpNKb8rFixAtHR0Uo/Bw4cyLe+IAjYuXMnevbsCUEQlMbj5+eHtLQ0XLp0SWmeESNGKB0FaNWqleKiwLe1atUKDx48wOvXrwFA9PZlZWWldE7c1NQULVu21Gi95x2pOXTokMopCG0NGjRIaVvI2yY13Q4LYmVlhRcvXhS5nTxi9zFPT0/4+fmptNG2bVvY2dkpteHr64vc3FycOHFCqb6+ls9ff/0FJycnODk5wcvLC4sWLUKvXr0UpyXzvL2fv3r1Ck+fPsV7770HACrbMPDm2p63tW3bFs+ePUN6ejqAN49vAIDPPvtMqd67321i9iFbW1v8888/ok9z9u3bFyYmJti6daui7Nq1a7hx4wYGDRqkKLO1tcX169dx584dUe2/rWvXrrC3t8eWLVsgCAK2bNlS5Os132VlZQUAGm/zeeukYsWKOo2jpGi7HWiqXJ+uWrFiBWrXrg0TExNUqlQJderUgZHRm7zvzp07EAQBtWrVUjvvu4fk3dzcVC6ounfvHurUqVPgBWB37txBWloanJ2d1U5/+zw78Obw5rvs7Ow0vgDu+vXr+Oqrr3Ds2DHFzpIn7/qMv//+G4DqXUgmJiYqd2vduXMHN2/ezPfQ4bvxvy2vnzp16qhM8/LywqlTpwoeTCFatmwp6sLjJ0+eIDU1FatXr8bq1avV1ilsfeQlFO7u7irlcrkcaWlpcHBwEL19ValSReUUg52dnUaHzD09PREUFIQlS5bg559/Rtu2bdGrVy989NFHWp2qAlTHnfcLXdPtsCAZGRk6/QIXu4+puyPvzp07uHLlisbbub6Wj4eHh+KOt3v37mHOnDl48uSJyp0+z58/R3h4OLZs2aISm7rrsAqK19raGn///TeMjIxUls273xFi9qHp06fjyJEjaNmyJWrWrImuXbti6NChaNOmTYHLwNHREZ07d8a2bdswe/ZsAG9OVZmYmKBv376KerNmzULv3r1Ru3Zt1K9fH926dcPw4cPRsGHDAtt/m1QqxYABAxAVFYWWLVviwYMH+Z5K01ZGRgYAzZOWvFNauvxDQJvrwXRF2+1AU+U6ySnol6BcLodEIsGBAwfUnvfMy77zaHv3hVwuh7OzM37++We109/9Us3vHKwgCIX2lZqaivbt28Pa2hqzZs1CjRo1YG5ujkuXLmH69Ola3Sosl8vRoEEDLFmyRO30d3/Zl2Z54//oo48UFzq/690vyPzWR2HrSez2VZT1Dry5G2PkyJHYu3cvDh8+jAkTJmDevHn4/fffla6x0lRR48mPTCbD7du3VZ4/UhRi9zF1+7JcLkeXLl0wbdo0tW3Url1b6bO+lk+FChWUbvFt06YNmjZtii+//BLLly9XlA8cOBBnzpzB1KlT0bhxY1hZWUEul6Nbt25q93NdxStmH6pbty5u3bqF3377DQcPHsTOnTvxww8/ICQkRO2FxW8bPHgwAgICcPnyZTRu3Bjbtm1D586dle6CateuHe7du6fY5teuXYvvvvsOq1atwqhRozQe09ChQ7Fq1SqEhYWhUaNG8Pb21nheTVy7dg3GxsZqk2t1vLy8AABXr17V6GGXeQlwVlaW2umZmZmibofXtaJsB5oo10lOQWrUqAFBEODp6anyBSamjXPnzkEmk+V7MWaNGjVw5MgRtGnTRme3qeaXlcfExODZs2fYtWsX2rVrpyh/+24yAKhWrRqANxc5d+zYUVH++vVrJCQkKP2ir1GjBv7880907txZ9F8Def3cunVL6bkoeWV504uLk5MTKlasiNzcXI2fxaMtXWxf7yps+Tdo0AANGjTAV199hTNnzqBNmzZYtWoVvvnmG530rws7duxAVlaWyumiotDFPlajRg1kZGTodLvQ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RAZM64cBenl5wcvLSydBjB8/HuPHj1c7LSYmRqXMx8cHv//+u076JiIiIsOkVZLzzz//4JdffkFiYiJycnKUpi1ZskQngREREWlNk5eQWlgAmze/uXOtsAu7xd2ITKWE6CTn6NGj6NWrF6pXr46//voL9evXR0JCAgRBQNOmTfURIxEREZFoopOc4OBgfPHFFwgPD0fFihWxc+dOODs7Y9iwYejWrZs+YiQi0oomr46wMAI2A7AJBrLkBdfl3/JEZYvou6tu3ryJESNGAABMTEyQlZUFKysrzJo1CwsWLNB5gERERETaEJ3kVKhQQXEdTuXKlXHv3j3FtKdPn+ouMiIiIqIiEH266r333sOpU6dQt25dfPDBB5gyZQquXr2KXbt24b333tNHjERERESiiU5ylixZgoyMDABAeHg4MjIysHXrVtSqVYt3VhEREVGpITrJqV69uuL/FSpUwKpVq3QaEBEREZEuiL4mp3r16nj27JlKeWpqqlICRERERFSSRCc5CQkJyM3NVSnPzs7Gw4cPdRIUERERUVFpfLrql19+Ufz/0KFDsLGxUXzOzc3F0aNH4eHhodPgiIiIiLSlcZLTp08fAIBEIoG/v7/SNKlUCg8PD43fPE5ERESliCavwRCjlLwGQ+MkRy5/8yhQT09P/PHHH3B0dNRbUERERERFJfruqvj4eH3EQURERKRTWr2F/OjRozh69ChSUlIUR3jyRERE6CQwIiL6H03ewyVG6TiZQKRfopOc8PBwzJo1C82bN0flypUh0fV5PCpcmB6+nkJ13yQREVFJEp3krFq1CpGRkRg+fLg+4iEiIiLSCdHPycnJyUHr1q31EQsRERGRzohOckaNGoWoqCh9xEJERESkM6JPV7169QqrV6/GkSNH0LBhQ0ilUqXpfEknERERlQaik5wrV66gcePGAIBr164pTeNFyERERFRaiE5yjh8/ro84iIiIiHRK9DU5ee7evYtDhw4hKysLACCUkkc4ExEREQFaJDnPnj1D586dUbt2bXzwwQd4/PgxAODjjz/GlClTdB4gERERkTZEJzmTJ0+GVCpFYmIiLC0tFeWDBg3CwYMHdRocERERkbZEX5Nz+PBhHDp0CFWqVFEqr1WrFv7++2+dBUZERERUFKKTnJcvXyodwcnz/PlzmJmZ6SQoIiKiotDkXV8WRsBmADbBQJa8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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Calculate the percentage of elements for each range\n", + "mid_less_than_64 = [x - y for x, y in zip(mid_anchors_total, mid_larger_than_64)]\n", + "mid_less_than_1024 = [x - y for x, y in zip(mid_anchors_total, mid_larger_than_1024)]\n", + "mid_between_64_and_1024 = [x - y for x, y in zip(mid_larger_than_64, mid_larger_than_1024)]\n", + "\n", + "# Plotting\n", + "plt.bar(MID_CUT_list, mid_less_than_64, color='b', label='<64')\n", + "plt.bar(MID_CUT_list, mid_between_64_and_1024, bottom=mid_less_than_64, color='g', label='64-1024')\n", + "plt.bar(MID_CUT_list, mid_larger_than_1024, bottom=mid_less_than_1024, color='r', label='>1024')\n", + "\n", + "plt.xlabel('MID_CUT_list')\n", + "plt.ylabel('Percentage of Anchors in Mid Kernel')\n", + "plt.title('Percentage of Elements in Different Ranges vs MID_CUT_list')\n", + "plt.legend()\n", + "plt.grid(True)\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": 188, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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FZhWSU758uWVTxnm83CIiIioONDqFvKRI0eia9eukEgkkEgkcHZ2zrVudna23oIjIiIi0pVWSU5wcDD69OmD+/fvo0uXLli3bh3s7e0NHBoRERGR7rR+GKCnpyc8PT0RFBSEnj17wtra2pBxERERERWI6LeQBwUFAQBevHiBO3fuAABq1KiB8uXL6zcyIiIiogIQ/ZyclJQUDB06FBUrVkTz5s3RvHlzVKxYEV988QVSUlIMESMRERGRaKKTnHHjxuH48ePYt28fEhISkJCQgL179+L48eMYP368IWIkIiIiEk306aqdO3fijz/+QMuWLZVlnTp1gpWVFXr16oUVK1boMz4iIiIineh0usrJyUmt3NHRkaeriIiIqMgQneT4+voiKCgIaWlpyrLU1FSEhITA19dXr8ERERER6Ur06aolS5bAz88PH3zwAerVqwcAuHr1KuRyOQ4ePKj3AImIiIh0ITrJqVOnDu7du4fff/8dt2/fBgD07dsX/fv3h5WVld4DJCIiItKF6CQHAKytrTF8+HB9x0JERESkN6KvySEiIiIqDpjkEBERkUnS6XQV5U4SrN/lCfpdHBERUYnBIzlERERkknRKchISErBmzRpMnjwZr169AgBERkbiyZMneg2OiIiISFeiT1f9888/aNu2Lezs7BAdHY3hw4ejbNmy2LVrF2JiYrBhwwZDxElEREQkiugjOYGBgRgyZAju3bsHuVyuLO/UqRNOnDih1+CIiIiIdCU6yfn7778xYsQItXIXFxfExsbqJSgiIiKighKd5MhkMiQlJamV3717F+XLl9dLUEREREQFJTrJ6dKlC6ZPn47MzEwAgEQiQUxMDCZNmoQePXroPUAiIiIiXYhOchYuXIjk5GQ4OjoiNTUVLVq0QNWqVVG6dGnMnDnTEDESERERiSb67io7OzuEh4fj1KlT+Oeff5CcnIwPP/wQbdu2NUR8RERERDrR+YnHH3/8MT7++GN9xkJERESkN6KTnKVLl2osl0gkkMvlqFq1Kpo3bw5zc/MCB0dERESkK9FJzk8//YQXL14gJSUFZcqUAQD8999/sLa2ho2NDZ4/f47KlSvj2LFjcHV11XvARERERNoQfeHxrFmz0KhRI9y7dw/x8fGIj4/H3bt34ePjgyVLliAmJgbOzs4YN26cIeIlIiIi0oroIzlTp07Fzp07UaVK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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Calculate the percentage of elements for each range\n", + "long_ratio_less_than_64 = [1 - x for x in long_ratio_larger_than_64]\n", + "long_ratio_less_than_1024 = [1 - y for y in long_ratio_larger_than_1024]\n", + "long_ratio_between_64_and_1024 = [x - y for x, y in zip(long_ratio_larger_than_64, long_ratio_larger_than_1024)]\n", + "\n", + "# Plotting\n", + "plt.bar(MID_CUT_list, long_ratio_less_than_64, color='b', label='<64')\n", + "plt.bar(MID_CUT_list, long_ratio_between_64_and_1024, bottom=long_ratio_less_than_64, color='g', label='64-1024')\n", + "plt.bar(MID_CUT_list, long_ratio_larger_than_1024, bottom=long_ratio_less_than_1024, color='r', label='>1024')\n", + "\n", + "plt.xlabel('MID_CUT_list')\n", + "plt.ylabel('Percentage of Elements')\n", + "plt.title('Percentage of Elements in Different Ranges vs MID_CUT_list')\n", + "plt.legend()\n", + "plt.grid(True)\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": 140, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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26YAypo1axRFURSr1apEREQojz/+uL3Nr7/+qgDKihUrSp17yy23KNHR0fav582bp6jVamXjxo2l2n3yyScKoGzatMl+DFDUarWyb9++MjUVFBSU+rqkpERp3ry50qtXL/ux7du3K4Dyf//3f6Xajh49WgGUSZMm2Y898MADSmhoqJKenl6q7V133aV4e3uXud6FoqKilJtvvllJS0tT0tLSlD179ij33HOPAijjx4+/bO2KoijTpk1TVCqVcuLECfuxUaNGKYAyderUUm3btGmjtGvXzv71Dz/8oADKe++9Zz9msViUXr16KYAyd+5c+/HevXsrLVq0UIqKiuzHrFarct111ymxsbGXvcfyfo9vu+02pVmzZpft62ISEhIUQHn77beVvXv3KoD998mHH36oeHh4KPn5+cqoUaMUd3f3Uud27969zDWjoqKU/v37X/J6M2bMUABl+fLl5apv+fLlCqDMmDGjXO3Xr1+vAMrixYsv+vn48eOVS/01k5aWVub3aEWcu/b69evtx0aNGqVERUXZv3788ccVLy8vxWw2X7KfxYsXl+lHiJpMRuyEwDZaFxwcTM+ePQHbI9I777yTb775xv5IsFevXgQEBPDtt9/az8vKymLNmjWlRuIWL15M06ZNadKkCenp6fZfvXr1AmD9+vWlrt29e3fi4uLK1OTq6lrqOjk5Odxwww3s2LHDfvzcY9tHHnmk1LmPPvpoqa8VReGHH35gwIABKIpSqq4+ffqQk5NTqt9LWb16NYGBgQQGBtKiRQvmzZvHfffdx9tvv33J2vPz80lPT+e6665DURR27txZpt+HH3641Nc33HADx44dK3WfOp2u1Ht8arWa8ePHlzovMzOTdevWcccdd2A0Gu33mJGRQZ8+fTh8+DBJSUmXvL/yfo99fHxITEy0j+g6olmzZrRs2ZJFixYBsHDhQm677Tbc3Nwc7vNC52bWGo3GcrXPzc0FqBWjdWD7PuXn57NmzRpnlyJEtZFgJ+o8i8XCN998Q8+ePUlISODIkSMcOXKETp06kZKSwtq1awHQarUMHTqU5cuX29+VW7JkCSaTqdRf+ocPH2bfvn32AHTuV6NGjQBITU0tdf0GDRpctK6VK1fSuXNnXFxc8PPzIzAwkI8//picnBx7mxMnTqBWq8v0ceFs3rS0NLKzs/n000/L1HXunbIL67qY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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "\n", + "fig, ax1 = plt.subplots()\n", + "\n", + "# Plot avg_range_mid\n", + "color = 'tab:blue'\n", + "ax1.set_xlabel('MID_CUT_list')\n", + "ax1.set_ylabel('avg_range_mid', color=color)\n", + "ax1.plot(MID_CUT_list, avg_range_mid, marker='o', color=color, linestyle='-', label='avg_range_mid')\n", + "ax1.tick_params(axis='y', labelcolor=color)\n", + "\n", + "# Create a twin axis for avg_range_short\n", + "ax2 = ax1.twinx()\n", + "color = 'tab:red'\n", + "ax2.set_ylabel('avg_range_short', color=color)\n", + "ax2.plot(MID_CUT_list, avg_range_short, marker='s', color=color, linestyle='-', label='avg_range_short')\n", + "ax2.tick_params(axis='y', labelcolor=color)\n", + "\n", + "fig.tight_layout() # Adjust layout to prevent clipping of labels\n", + "plt.title('Average Range vs MID_CUT_list')\n", + "plt.grid(True)\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[0.27324480968881387, 0.2732439499409883, 0.2732441043488688, 0.27324472765034524, 0.2732448385061006, 0.2732448079539508, 0.2732448385061006]\n", + "[337.4520629293166, 377.98481955068604, 414.66843947960416, 452.74455885586076, 484.31032239941436, 511.4723277400877, 530.4147211089912]\n", + "[1.4844603719646354, 1.484417987583865, 1.484424329880518, 1.484456630712539, 1.4844610114824295, 1.4844606340443962, 1.4844610114824295]\n", + "[4490384, 6213671, 7792645, 9398573, 10859087, 12234518, 13268558]\n", + "[61704426, 67802586, 73509943, 78080017, 82088873, 85566675, 88477861]\n" + ] + } + ], + "source": [ + "import csv\n", + "LONG_CUT_list = [40,50,60,70,80,90,100]\n", + "\n", + "short_kernel_it = []\n", + "avg_range_short = []\n", + "avg_range_mid = []\n", + "mid_anchors_total = []\n", + "mid_larger_than_64 = []\n", + "mid_larger_than_1024 = []\n", + "long_anchors_total = []\n", + "long_larger_than_64 = []\n", + "long_larger_than_1024 = []\n", + "short_anchors_total = []\n", + "short_larger_than_64 = []\n", + "short_larger_than_1024 = []\n", + "\n", + "for i in LONG_CUT_list:\n", + " range_file = \"../range_dis_l%d.csv\" % i;\n", + " long_range_file = \"../long_range_dis_l%d.csv\" % i;\n", + " mid_range_file = \"../last_mid_range_dis_l%d.csv\" % i;\n", + " read_csv_files_range_dis(range_file, mid_range_file, long_range_file)\n", + "\n", + "print(short_kernel_it)\n", + "print(avg_range_mid)\n", + "print(avg_range_short)\n", + "print(mid_larger_than_1024)\n", + "print(mid_larger_than_64)" + ] + }, + { + "cell_type": "code", + "execution_count": 195, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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ubmhcuXKGi99hYWF4bvvvsMPP/yAc+fO4YMPPkBOTo505+SQIUM0BuSrff/99wgODtYaRJ+dnY0JEybg8OHDuHLlCmJjY/Haa6+hdu3aCAwM1Ds+IiIiIkPS+27HyMhIrFy5Eh9//DEmT56Mzz77DFeuXMHmzZsxdepUvQMYMGAAbt26halTpyI1NRW+vr7YsWMHXF1dAQDXrl2DXK6ZI164cAEHDhzArl27tNpTKBQ4deoUfvjhB2RmZsLDwwPdunXDjBkziry0SERERGRKeidfP/30E7777jv07NkT06ZNw6BBg1CrVi00adIEhw8fxujRo/UOIjQ0FKGhoUXOi4uL0yqrW7cuirtJ09raGjt37tQ7BiIiIiJT0PuyY2pqKho3bgwAsLOzkx642qtXL2zbts2w0RERERFVMHonX9WrV0dKSgoAoFatWtKlv6NHj/KyHhEREdFT6J189enTR7o78aOPPsKUKVPg4+ODIUOG4J133jF4gEREREQVid5jvubMmSP9PmDAANSsWRPx8fHw8fFB7969DRocERERUUWjd/L1JH9//6c+k4uIiIiIHtEp+fr99991bvDVV18tdTBEREREFZ1OyZf6ex2fRiaTobCQX15MREREVBydki+VSmXsOIiIiIheCHrf7UhEREREpaf3gPvp06eXOL80XzFERERE9KLQO/natGmTxrRSqURycjLMzMxQq1YtJl9EREREJdA7+Tpx4oRWWVZWFoYOHYo+ffoYJCgiIiKiisogY77s7e0RGRmJKVOmGKI5IiIiogrLYAPu7927J33JNhEREREVTe/LjkuXLtWYFkIgJSUFa9euRffu3Q0WGBEREVFFpHfytWjRIo1puVyOKlWqICQkBOHh4QYLjIiIiKgi0jv5Sk5ONkYcRERERC8EPmSViIiIyIT0PvOVl5eHL7/8Env37kV6errWVw8dP37cYMERERERVTR6J1/Dhw/Hrl278MYbb6BVq1aQyWTGiIuIiIioQtI7+dq6dSv++OMPtG3b1hjxEBEREVVoeo/5qlatGipVqmSMWIiIiIgqPL2TrwULFuCTTz7B1atXjREPERERUYWm92XHli1bIi8vDy+99BJsbGxgbm6uMf/OnTsGC46IiIiootE7+Ro0aBBu3LiBWbNmwdXVlQPuiYiIiPSgd/J16NAhxMfHo2nTpsaIh4iI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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "\n", + "bin_size = 50\n", + "# Determine the number of bins (each representing 100 elements)\n", + "num_bins = len(ranges_numeric) // bin_size\n", + "\n", + "# Calculate the cumulative sum for each bin\n", + "cumulative_sum = [sum(ranges_numeric[i*bin_size:(i+1)*bin_size]) for i in range(num_bins)]\n", + "cumulative_long_sum = [sum(long_ranges_numeric[i*bin_size:(i+1)*bin_size]) for i in range(num_bins)]\n", + "cumulative_mid_sum = [sum(mid_ranges_numeric[i*bin_size:(i+1)*bin_size]) for i in range(num_bins)]\n", + "cumulative_mid_sum = [x + y for x, y in zip(cumulative_mid_sum, cumulative_long_sum)]\n", + "\n", + "# Plotting\n", + "plt.bar(range(num_bins), cumulative_sum, color='skyblue')\n", + "\n", + "# # Plot the subset (mid) with a different color\n", + "# for i, value in enumerate(cumulative_mid_sum):\n", + "# plt.bar(i, value, color='green')\n", + "\n", + "# # Plot the subset (long) with a different color\n", + "# for i, value in enumerate(cumulative_long_sum):\n", + "# plt.bar(i, value, color='orange')\n", + " \n", + " \n", + "# Add vertical line at x=64 and color it red\n", + "plt.axvline(x=64.0/bin_size, color='red')\n", + "\n", + "# Add vertical line at x=1000 and color it red\n", + "plt.axvline(x=1000/bin_size, color='blue')\n", + "\n", + "# Set upper limit of y-axis to 20m\n", + "plt.ylim(0, 20000000)\n", + "\n", + "# Label the first bar\n", + "# plt.text(0, cumulative_sum[0], str(cumulative_sum[0]), ha='right')\n", + "plt.xlabel('Bins (Each bin represents %d elements)' % bin_size)\n", + "plt.ylabel('Cumulative Sum of Elements')\n", + "plt.title('Histogram of Range Distribution (Each bin represents %d elements)' % bin_size)\n", + "plt.grid(True)\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": 192, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[0, 380090416, 185390118, 97804398, 65480800, 51925885, 44354352, 39754498, 36480512, 34068933]\n", + "[0, 238157, 266756, 309327, 362744, 426585, 496920, 578872, 664400, 749259]\n", + "[0, 32730, 40474, 47427, 57552, 67300, 74724, 85841, 97984, 111186]\n", + "Precent of Workload > 16 bases: 0.9932645365174807\n", + "Precent of Workload > 64 bases: 0.9863263540183071\n", + "Precent of Workload > 64 bases in short: 0.8996161386626471\n", + "Precent of Workload in Long 0.596239090529109\n", + "Precent of Workload in Mid 0.2807532365863811\n", + "Precent of Workload in Short 0.12300767288450998\n", + "Avg range: 8.842975553696157 899.5018304893736 1705.7212686890798\n" + ] + } + ], + "source": [ + "\n", + "print(workload_range_dis[0:10])\n", + "print(workload_mid_range_dis[0:10])\n", + "print(workload_long_range_dis[0:10])\n", + "\n", + "\n", + "print(\"Precent of Workload > 16 bases: \", sum(workload_range_dis[16:]) / sum(workload_range_dis))\n", + "print(\"Precent of Workload > 64 bases: \", sum(workload_range_dis[64:]) / sum(workload_range_dis))\n", + "print(\"Precent of Workload > 64 bases in short: \", sum(workload_short_range_dis[64:]) / sum(workload_short_range_dis))\n", + "print(\"Precent of Workload in Long \", sum(workload_long_range_dis) / sum(workload_range_dis))\n", + "print(\"Precent of Workload in Mid \", sum(workload_mid_range_dis) / sum(workload_range_dis))\n", + "print(\"Precent of Workload in Short \", 1 - (sum(workload_long_range_dis) + sum(workload_mid_range_dis)) / sum(workload_range_dis))\n", + "print(\"Avg range: \", (sum(workload_range_dis) - sum(workload_long_range_dis) - sum(workload_mid_range_dis))/(sum(ranges_numeric) - sum(long_ranges_numeric) - sum(mid_ranges_numeric))\n", + " ,sum(workload_mid_range_dis) / sum(mid_ranges_numeric), sum(workload_long_range_dis) / sum(long_ranges_numeric))" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "ename": "NameError", + "evalue": "name 'workload_range_dis' is not defined", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[0;32mIn[4], line 3\u001b[0m\n\u001b[1;32m 1\u001b[0m bin_size \u001b[38;5;241m=\u001b[39m \u001b[38;5;241m50\u001b[39m\n\u001b[1;32m 2\u001b[0m \u001b[38;5;66;03m# Determine the number of bins (each representing 100 elements)\u001b[39;00m\n\u001b[0;32m----> 3\u001b[0m num_bins \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mlen\u001b[39m(\u001b[43mworkload_range_dis\u001b[49m) \u001b[38;5;241m/\u001b[39m\u001b[38;5;241m/\u001b[39m bin_size\n\u001b[1;32m 5\u001b[0m \u001b[38;5;66;03m# Calculate the cumulative sum for each bin\u001b[39;00m\n\u001b[1;32m 6\u001b[0m cul_wl_range_dis \u001b[38;5;241m=\u001b[39m [\u001b[38;5;28msum\u001b[39m(workload_range_dis[i\u001b[38;5;241m*\u001b[39mbin_size:(i\u001b[38;5;241m+\u001b[39m\u001b[38;5;241m1\u001b[39m)\u001b[38;5;241m*\u001b[39mbin_size]) \u001b[38;5;28;01mfor\u001b[39;00m i \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mrange\u001b[39m(num_bins)]\n", + "\u001b[0;31mNameError\u001b[0m: name 'workload_range_dis' is not defined" + ] + } + ], + "source": [ + "bin_size = 50\n", + "# Determine the number of bins (each representing 100 elements)\n", + "num_bins = len(workload_range_dis) // bin_size\n", + "\n", + "# Calculate the cumulative sum for each bin\n", + "cul_wl_range_dis = [sum(workload_range_dis[i*bin_size:(i+1)*bin_size]) for i in range(num_bins)]\n", + "cul_wl_long_range_dis = [sum(workload_long_range_dis[i*bin_size:(i+1)*bin_size]) for i in range(num_bins)]\n", + "cul_wl_mid_range_dis = [sum(workload_mid_range_dis[i*bin_size:(i+1)*bin_size]) for i in range(num_bins)]\n", + "cul_wl_mid_range_dis = [x + y for x, y in zip(cul_wl_mid_range_dis, cul_wl_long_range_dis)]\n", + "\n", + "\n", + "# Plotting\n", + "plt.bar(range(num_bins), cul_wl_range_dis, color='skyblue')\n", + "\n", + "# Plot the subset (mid) with a different color\n", + "# for i, value in enumerate(cul_wl_mid_range_dis):\n", + "# plt.bar(i, value, color='green')\n", + "\n", + "# # Plot the subset (long) with a different color\n", + "# for i, value in enumerate(cul_wl_long_range_dis):\n", + "# plt.bar(i, value, color='orange')\n", + "\n", + "# Add vertical line at x=64 and color it red\n", + "plt.axvline(x=64.0/50, color='red')\n", + "\n", + "# Add vertical line at x=1000 and color it red\n", + "plt.axvline(x=1000/50, color='blue')\n", + "\n", + "# Set upper limit of y-axis to 20m\n", + "plt.ylim(0, 4000000000)\n", + "\n", + "# Label the first bar\n", + "# plt.text(0, cumulative_sum[0], str(cumulative_sum[0]), ha='right')\n", + "plt.xlabel('Range (Each bin represents %d in range)' % bin_size)\n", + "plt.ylabel('Cumulative Workload')\n", + "plt.title('Workload Distribution w.r.t. Anchor Range(Each bin represents %d elements)' % bin_size)\n", + "plt.grid(True)\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'seg_len': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160, 170, 180, 190, 200, 210, 220, 230, 240, 250, 260, 270, 280, 290, 300, 310, 320, 330, 340, 350, 360, 370, 380, 390, 400, 410, 420, 430, 440, 450, 460, 470, 480, 490, 500, 510, 520, 530, 540, 550, 560, 570, 580, 590, 600, 610, 620, 630, 640, 650, 660, 670, 680, 690, 700, 710, 720, 730, 740, 750, 760, 770, 780, 790, 800, 810, 820, 830, 840, 850, 860, 870, 880, 890, 900, 910, 920, 930, 940, 950, 960, 970, 980, 990, 1000, 1010, 1020, 1030, 1040, 1050, 1060, 1070, 1080, 1090, 1100, 1110, 1120, 1130, 1140, 1150, 1160, 1170, 1180, 1190, 1200, 1210, 1220, 1230, 1240, 1250, 1260, 1270, 1280, 1290, 1300, 1310, 1320, 1330, 1340, 1350, 1360, 1370, 1380, 1390, 1400, 1410, 1420, 1430, 1440, 1450, 1460, 1470, 1480, 1490, 1500, 1510, 1520, 1530, 1540, 1550, 1560, 1570, 1580, 1590, 1600, 1610, 1620, 1630, 1640, 1650, 1660, 1670, 1680, 1690, 1700, 1710, 1720, 1730, 1740, 1750, 1760, 1770, 1780, 1790, 1800, 1810, 1820, 1830, 1840, 1850, 1860, 1870, 1880, 1890, 1900, 1910, 1920, 1930, 1940, 1950, 1960, 1970, 1980, 1990, 2000, 2010, 2020, 2030, 2040, 2050, 2060, 2070, 2080, 2090, 2100, 2110, 2120, 2130, 2140, 2150, 2160, 2170, 2180, 2190, 2200, 2210, 2220, 2230, 2240, 2250, 2260, 2270, 2280, 2290, 2300, 2310, 2320, 2330, 2340, 2350, 2360, 2370, 2380, 2390, 2400, 2410, 2420, 2430, 2440, 2450, 2460, 2470, 2480, 2490, 2500, 2510, 2520, 2530, 2540, 2550, 2560, 2570, 2580, 2590, 2600, 2610, 2620, 2630, 2640, 2650, 2660, 2670, 2680, 2690, 2700, 2710, 2720, 2730, 2740, 2750, 2760, 2770, 2780, 2790, 2800, 2810, 2820, 2830, 2840, 2850, 2860, 2870, 2880, 2890, 2900, 2910, 2920, 2930, 2940, 2950, 2960, 2970, 2980, 2990, 3000, 3010, 3020, 3030, 3040, 3050, 3060, 3070, 3080, 3090, 3100, 3110, 3120, 3130, 3140, 3150, 3160, 3170, 3180, 3190, 3200, 3210, 3220, 3230, 3240, 3250, 3260, 3270, 3280, 3290, 3300, 3310, 3320, 3330, 3340, 3350, 3360, 3370, 3380, 3390, 3400, 3410, 3420, 3430, 3440, 3450, 3460, 3470, 3480, 3490, 3500, 3510, 3520, 3530, 3540, 3550, 3560, 3570, 3580, 3590, 3600, 3610, 3620, 3630, 3640, 3650, 3660, 3670, 3680, 3690, 3700, 3710, 3720, 3730, 3740, 3750, 3760, 3770, 3780, 3790, 3800, 3810, 3820, 3830, 3840, 3850, 3860, 3870, 3880, 3890, 3900, 3910, 3920, 3930, 3940, 3950, 3960, 3970, 3980, 3990, 4000, 4010, 4020, 4030, 4040, 4050, 4060, 4070, 4080, 4090, 4100, 4110, 4120, 4130, 4140, 4150, 4160, 4170, 4180, 4190, 4200, 4210, 4220, 4230, 4240, 4250, 4260, 4270, 4280, 4290, 4300, 4310, 4320, 4330, 4340, 4350, 4360, 4370, 4380, 4390, 4400, 4410, 4420, 4430, 4440, 4450, 4460, 4470, 4480, 4490, 4500, 4510, 4520, 4530, 4540, 4550, 4560, 4570, 4580, 4590, 4600, 4610, 4620, 4630, 4640, 4650, 4660, 4670, 4680, 4690, 4700, 4710, 4720, 4730, 4740, 4750, 4760, 4770, 4780, 4790, 4800, 4810, 4820, 4830, 4840, 4850, 4860, 4870, 4880, 4890, 4900, 4910, 4920, 4930, 4940, 4950, 4960, 4970, 4980, 4990, 5000, 0], 'sc_pairs': [1451670689, 613181299, 568356128, 674102799, 932969857, 1521042310, 2303006315, 2864751658, 2546465706, 1661846771, 924058335, 694517700, 616875762, 557122231, 688599394, 543951867, 556802567, 583184395, 670911519, 599102236, 6405597539, 5925198166, 6168152676, 6096282938, 6165475981, 5488171150, 4962916075, 3856673093, 3260455805, 3058574087, 1782436146, 3015530241, 1298968520, 755362931, 584932112, 1333851187, 1088231240, 883704715, 441135000, 1039773429, 822065952, 2024091506, 347627661, 144466384, 167105010, 158689277, 490529429, 271235480, 1289346260, 295940672, 392852811, 249756261, 154679974, 425611728, 78505257, 244114103, 178221174, 211959258, 0, 0, 336770524, 120983751, 0, 0, 1295674869, 154421842, 0, 266719583, 1577314857, 0, 265146192, 770824544, 0, 0, 651454342, 0, 325083871, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1029457004, 960363363, 551135677, 0, 349882748, 0, 381374599, 0, 0, 1282631145, 0, 0, 658163034, 1293831795, 0, 790207861, 439746117, 451695263, 1237012484, 0, 0, 0, 786405279, 569040198, 0, 0, 1758267248, 1825384859, 1507163659, 0, 1933830031, 1680695255, 2014499839, 0, 721037322, 0, 0, 3345348809, 727303963, 1922426868, 771150439, 1205069222, 0, 1402244004, 0, 0, 0, 0, 2900623330, 0, 0, 3038426068, 0, 0, 984562116, 0, 0, 0, 0, 0, 0, 0, 5196154950, 0, 0, 0, 0, 0, 0, 3439894017, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2465188264, 0, 3206858364, 0, 2629782562, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3040384243, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7658240445, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 'anchors': [2150545312, 53131104, 15035832, 9288032, 8024632, 8710427, 9978669, 10345426, 8393974, 5531815, 3156567, 2453959, 2225756, 1894193, 2007045, 1789045, 1715866, 1661201, 1479503, 1394168, 11392634, 8513016, 6594519, 6096911, 4926205, 4274565, 3726120, 3117720, 2411938, 2235322, 1227392, 1661821, 1266791, 1190940, 688279, 1086356, 847387, 897036, 738855, 329723, 808042, 974746, 501756, 521208, 542727, 419837, 583202, 757249, 627776, 807944, 999940, 513029, 354302, 729094, 186879, 387074, 395770, 404472, 0, 0, 437768, 221184, 0, 0, 948727, 245250, 0, 251904, 1034233, 0, 271360, 272889, 0, 0, 577532, 0, 301568, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1047047, 712695, 359424, 0, 371200, 0, 383487, 0, 0, 795139, 0, 0, 413184, 835069, 0, 429055, 434688, 440322, 885247, 0, 0, 0, 462334, 467457, 0, 0, 482304, 491520, 985087, 0, 502784, 1019390, 517119, 0, 525827, 0, 0, 1078271, 544256, 1100788, 555521, 559104, 0, 573441, 0, 0, 0, 0, 1189886, 0, 0, 1222152, 0, 0, 627199, 0, 0, 0, 0, 0, 0, 0, 2008574, 0, 0, 0, 0, 0, 0, 705025, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 799234, 0, 813052, 0, 824320, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 884224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1989124, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 'segs': [4194747, 51844, 9779, 4530, 3132, 2833, 2782, 2524, 1821, 1080, 560, 399, 334, 264, 261, 218, 197, 180, 152, 136, 895, 475, 284, 215, 148, 111, 85, 64, 45, 38, 19, 24, 17, 15, 8, 12, 9, 9, 7, 3, 7, 8, 4, 4, 4, 3, 4, 5, 4, 5, 6, 3, 2, 4, 1, 2, 2, 2, 0, 0, 2, 1, 0, 0, 4, 1, 0, 1, 4, 0, 1, 1, 0, 0, 2, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 2, 1, 0, 1, 0, 1, 0, 0, 2, 0, 0, 1, 2, 0, 1, 1, 1, 2, 0, 0, 0, 1, 1, 0, 0, 1, 1, 2, 0, 1, 2, 1, 0, 1, 0, 0, 2, 1, 2, 1, 1, 0, 1, 0, 0, 0, 0, 2, 0, 0, 2, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 139867549361536, 0]}\n" + ] + } + ], + "source": [ + "import csv\n", + "sc_pair_dis = {}\n", + "with open(\"../sc_pair_dis.csv\") as csvfile:\n", + " reader = csv.reader(csvfile, delimiter=',')\n", + " for row in reader:\n", + " sc_pair_dis[row[0]] = [int(x) if x.isdigit() else 0 for x in row[1:]]\n", + " print(sc_pair_dis)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 30, 40, 50, 60, 70, 80, 90, 100]\n", + "[1451670689, 613181299, 568356128, 674102799, 932969857, 1521042310, 2303006315, 2864751658, 2546465706, 1661846771, 924058335, 694517700, 616875762, 557122231, 688599394, 543951867, 556802567, 583184395, 670911519, 599102236, 6405597539, 5925198166, 6168152676, 6096282938, 6165475981, 5488171150, 4962916075, 3856673093, 3260455805]\n", + "[2150545312, 53131104, 15035832, 9288032, 8024632, 8710427, 9978669, 10345426, 8393974, 5531815, 3156567, 2453959, 2225756, 1894193, 2007045, 1789045, 1715866, 1661201, 1479503, 1394168, 11392634, 8513016, 6594519, 6096911, 4926205, 4274565, 3726120, 3117720, 2411938]\n", + "[4194747, 51844, 9779, 4530, 3132, 2833, 2782, 2524, 1821, 1080, 560, 399, 334, 264, 261, 218, 197, 180, 152, 136, 895, 475, 284, 215, 148, 111, 85, 64, 45]\n" + ] + } + ], + "source": [ + "print(sc_pair_dis['seg_len'][:29])\n", + "print(sc_pair_dis['sc_pairs'][:29])\n", + "print(sc_pair_dis['anchors'][:29])\n", + "print(sc_pair_dis['segs'][:29])" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "percentage of anchors\n", + "[0.675024460493674, 11.540910179468508, 37.800111626679524, 72.57757068451099, 116.26325755498819, 174.62316256137615, 230.79293591159302, 276.90997528762955, 303.3683099328161, 300.416187273074, 292.74155593719377, 283.0192761981761, 277.1533636211696, 294.12115396899895, 343.0911583945552, 304.0459390345128, 324.5023603241745, 351.06190942577086, 453.4708743409104, 429.7202603990337, 562.2578184289954, 696.0163314623161, 935.3453490694318, 999.8969868512104, 1251.5670746548305, 1283.9133689626897, 1331.925991379773, 1237.0171449007607, 1351.7991776737213, 1368.2923923264746, 1452.2142445119407, 1814.5938948900032, 1025.4008119729301, 634.2577552185669, 849.8473903751241, 1227.821438828524, 1284.2198900856397, 985.1385172947352, 597.0521956270175, 3153.475581018006, 1017.3554740966435, 2076.532251478847, 692.8221306770622, 277.1760679037927, 307.89883311499153, 377.9783034844475, 841.0969595440345, 358.18532609485123, 2053.8317170455703, 366.28859425900805, 392.87638358301496, 486.82678951872117, 436.57663236448, 583.7542593959078, 420.0860289278089, 630.665203552809, 450.3150162973444, 524.0393846792856, 769.2899526689936, 546.9823811848959, 1365.6983189052278, 629.6507319062182, 1058.8144015180387, 1525.1059065026932, 977.1012382075472, 2824.6816251296314, 1127.9969629388502, 1077.978668161078, 983.2003759143572, 1347.5096121061604, 1533.385853476674, 942.5720581896552, 994.4915968468292, 1613.0904722318992, 1592.9054222815985, 1549.3711238233009, 1841.740245423081, 1011.6362011373675, 1025.8294225589455, 1397.3642203814302, 1700.9462401640374, 1217.3102509963483, 3645.5580878450105, 3713.7550028483074, 1529.980254535894, 3846.244174436736, 1648.726449150963, 3895.6213927548592, 1371.2443864617069, 3102.512085551777, 1336.3269545948965, 1746.4097246699637, 1388.1571335737083, 2155.357897636218, 2445.3152181305486, 2437.73212727942, 2486.1278040701973, 1569.7762847198417, 2586.9870614674887, 4879.109275557605, 3084.4386800361344, 3944.222957449216, 3190.2447617430125, 3438.477402784815]\n", + "[0.9799908606544164, 0.012111969131813567, 0.0022846027725485084, 0.0010583137907398245, 0.0007317083427366733, 0.0006618549600807776, 0.0006499401690592034, 0.0005896653438912399, 0.00042542812647620754, 0.00025231322163333565, 0.00013082907788395181, 9.321571799231566e-05, 7.803020002364268e-05, 6.167656528814871e-05, 6.0975695228056105e-05, 5.0929891033395525e-05, 4.602380061274733e-05, 4.2052203605555935e-05, 3.5510749711358347e-05, 3.177277605753115e-05, 2.090929012609587e-05, 1.1097109284799482e-05, 6.634903235543271e-06, 5.022902097330292e-06, 3.457625629790155e-06, 2.593219222342616e-06, 1.985798503595697e-06, 1.4951894615308778e-06, 1.0513050901388984e-06, 8.877687427839586e-07, 4.438843713919793e-07, 5.606960480740792e-07, 3.971597007191394e-07, 3.504350300462995e-07, 1.8689868269135972e-07, 2.803480240370396e-07, 2.1026101802777968e-07, 2.1026101802777968e-07, 1.6353634735493976e-07, 7.00870060092599e-08, 1.6353634735493976e-07, 1.8689868269135972e-07, 9.344934134567986e-08, 9.344934134567986e-08, 9.344934134567986e-08, 7.00870060092599e-08, 9.344934134567986e-08, 1.1681167668209982e-07, 9.344934134567986e-08, 1.1681167668209982e-07, 1.401740120185198e-07, 7.00870060092599e-08, 4.672467067283993e-08, 9.344934134567986e-08, 2.3362335336419965e-08, 4.672467067283993e-08, 4.672467067283993e-08, 4.672467067283993e-08, 4.672467067283993e-08, 2.3362335336419965e-08, 9.344934134567986e-08, 2.3362335336419965e-08, 2.3362335336419965e-08, 9.344934134567986e-08, 2.3362335336419965e-08, 2.3362335336419965e-08, 4.672467067283993e-08, 2.3362335336419965e-08, 7.00870060092599e-08, 4.672467067283993e-08, 2.3362335336419965e-08, 2.3362335336419965e-08, 2.3362335336419965e-08, 4.672467067283993e-08, 2.3362335336419965e-08, 4.672467067283993e-08, 2.3362335336419965e-08, 2.3362335336419965e-08, 2.3362335336419965e-08, 4.672467067283993e-08, 2.3362335336419965e-08, 2.3362335336419965e-08, 2.3362335336419965e-08, 2.3362335336419965e-08, 4.672467067283993e-08, 2.3362335336419965e-08, 4.672467067283993e-08, 2.3362335336419965e-08, 2.3362335336419965e-08, 4.672467067283993e-08, 2.3362335336419965e-08, 4.672467067283993e-08, 2.3362335336419965e-08, 2.3362335336419965e-08, 2.3362335336419965e-08, 4.672467067283993e-08, 4.672467067283993e-08, 2.3362335336419965e-08, 7.00870060092599e-08, 2.3362335336419965e-08, 2.3362335336419965e-08, 2.3362335336419965e-08, 2.3362335336419965e-08, 2.3362335336419965e-08]\n" + ] + }, + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from matplotlib import pyplot as plt\n", + "# Data\n", + "seg_len = sc_pair_dis['seg_len'][0:200]\n", + "sc_pairs = sc_pair_dis['sc_pairs'][0:200]\n", + "anchors = sc_pair_dis['anchors'][0:200]\n", + "segs = sc_pair_dis['segs'][0:200]\n", + "avg_range = [ (x/y) if (y!=0) else 0 for x,y in zip(sc_pairs, anchors) ]\n", + "\n", + "\n", + "filtered_indices = [i for i in range(len(sc_pairs)) if sc_pairs[i] != 0 and anchors[i] != 0 and segs[i] != 0]\n", + "seg_len_filtered = [seg_len[i]*512 for i in filtered_indices]\n", + "avg_range_filtered = [avg_range[i] for i in filtered_indices]\n", + "segs_filtered = [segs[i] for i in filtered_indices]\n", + "anchors_filtered = [anchors[i] for i in filtered_indices]\n", + "segs_filtered = [x / sum(segs_filtered) for x in segs_filtered]\n", + "segs_filtered[20:] = [x/10 for x in segs_filtered[20:]]\n", + "\n", + "\n", + "# Plotting\n", + "fig, ax1 = plt.subplots()\n", + "\n", + "# Plot sc_pairs on the first y-axis\n", + "ax1.scatter(seg_len_filtered, avg_range_filtered, label='avg_range', color='blue')\n", + "ax1.set_xlabel('seg_len')\n", + "ax1.set_ylabel('avg_range', color='blue')\n", + "ax1.tick_params(axis='y', labelcolor='blue')\n", + "ax1.set_ylim(0, 5000)\n", + "# ax1.set_xscale('log') # Set logarithmic scale for avg_range\n", + "\n", + "print(\"percentage of anchors\")\n", + "anchors_filtered = [x / sum(anchors_filtered) for x in anchors_filtered]\n", + "print(avg_range_filtered)\n", + "\n", + "# # Plot sc_pairs on the first y-axis\n", + "# ax1.plot(seg_len, sc_pairs, label='sc_pairs', color='blue')\n", + "# ax1.set_xlabel('seg_len')\n", + "# ax1.set_ylabel('sc_pairs', color='blue')\n", + "# ax1.tick_params(axis='y', labelcolor='blue')\n", + "\n", + "# Create a second y-axis for anchors\n", + "ax2 = ax1.twinx()\n", + "ax2.plot(seg_len_filtered, anchors_filtered, label='anchors', color='green')\n", + "ax2.set_ylabel('anchors', color='green')\n", + "ax2.tick_params(axis='y', labelcolor='green')\n", + "ax2.set_yscale('log') # Set logarithmic scale for segs\n", + "ax2.set_ylim(0.00000001, 5)\n", + "\n", + "# Create a third y-axis for segs\n", + "ax3 = ax1.twinx()\n", + "ax3.plot(seg_len_filtered, segs_filtered, label='segs', color='red')\n", + "ax3.set_ylabel('segs', color='red')\n", + "ax3.tick_params(axis='y', labelcolor='red')\n", + "ax3.set_yscale('log') # Set logarithmic scale for segs\n", + "ax3.set_ylim(0.00000001, 5)\n", + "print(segs_filtered)\n", + "# print([x / sum(segs_filtered) for x in segs_filtered])\n", + "\n", + "# Add legend\n", + "lines = ax1.get_lines() + ax3.get_lines()\n", + "ax1.legend(lines, [line.get_label() for line in lines], loc='upper left')\n", + "\n", + "# Add horizontal lines\n", + "line_64 = ax1.axhline(y=64, color='c', linestyle='--')\n", + "line_1024 = ax1.axhline(y=1024, color='c', linestyle='--')\n", + "# # Calculate midpoints of horizontal lines\n", + "# midpoint_64 = (line_64.get_ydata()[0] + line_64.get_ydata()[1]) / 2\n", + "# midpoint_1024 = (line_1024.get_ydata()[0] + line_1024.get_ydata()[1]) / 2\n", + "# # # Add labels at midpoints\n", + "# ax1.text(0, midpoint_64, 'avg_range = 64', ha='right', va='center', color='c')\n", + "# ax1.text(0, midpoint_1024, 'avg_range = 1024', ha='right', va='center', color='c')\n", + "\n", + "plt.title('avg_range and number of segs with respect to seg_len')\n", + "plt.grid(True)\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "percentage of anchors\n", + "[0.675024460493674, 11.540910179468508, 37.800111626679524, 72.57757068451099, 116.26325755498819, 174.62316256137615, 230.79293591159302, 276.90997528762955, 303.3683099328161, 300.416187273074, 292.74155593719377, 283.0192761981761, 277.1533636211696, 294.12115396899895, 343.0911583945552, 304.0459390345128, 324.5023603241745, 351.06190942577086, 453.4708743409104, 429.7202603990337, 562.2578184289954, 696.0163314623161, 935.3453490694318, 999.8969868512104, 1251.5670746548305, 1283.9133689626897, 1331.925991379773, 1237.0171449007607, 1351.7991776737213, 1368.2923923264746, 1452.2142445119407, 1814.5938948900032, 1025.4008119729301, 634.2577552185669, 849.8473903751241, 1227.821438828524, 1284.2198900856397, 985.1385172947352, 597.0521956270175, 3153.475581018006, 1017.3554740966435, 2076.532251478847, 692.8221306770622, 277.1760679037927, 307.89883311499153, 377.9783034844475, 841.0969595440345, 358.18532609485123, 2053.8317170455703, 366.28859425900805, 392.87638358301496, 486.82678951872117, 436.57663236448, 583.7542593959078, 420.0860289278089, 630.665203552809, 450.3150162973444, 524.0393846792856, 769.2899526689936, 546.9823811848959, 1365.6983189052278, 629.6507319062182, 1058.8144015180387, 1525.1059065026932, 977.1012382075472, 2824.6816251296314, 1127.9969629388502, 1077.978668161078, 983.2003759143572, 1347.5096121061604, 1533.385853476674, 942.5720581896552, 994.4915968468292, 1613.0904722318992, 1592.9054222815985, 1549.3711238233009, 1841.740245423081, 1011.6362011373675, 1025.8294225589455, 1397.3642203814302, 1700.9462401640374, 1217.3102509963483, 3645.5580878450105, 3713.7550028483074, 1529.980254535894, 3846.244174436736, 1648.726449150963, 3895.6213927548592, 1371.2443864617069, 3102.512085551777, 1336.3269545948965, 1746.4097246699637, 1388.1571335737083, 2155.357897636218, 2445.3152181305486, 2437.73212727942, 2486.1278040701973, 1569.7762847198417, 2586.9870614674887, 4879.109275557605, 3084.4386800361344, 3944.222957449216, 3190.2447617430125, 3438.477402784815]\n", + "[0.9799908606544164, 0.012111969131813567, 0.0022846027725485084, 0.0010583137907398245, 0.0007317083427366733, 0.0006618549600807776, 0.0006499401690592034, 0.0005896653438912399, 0.00042542812647620754, 0.00025231322163333565, 0.00013082907788395181, 9.321571799231566e-05, 7.803020002364268e-05, 6.167656528814871e-05, 6.0975695228056105e-05, 5.0929891033395525e-05, 4.602380061274733e-05, 4.2052203605555935e-05, 3.5510749711358347e-05, 3.177277605753115e-05, 2.090929012609587e-05, 1.1097109284799482e-05, 6.634903235543271e-06, 5.022902097330292e-06, 3.457625629790155e-06, 2.593219222342616e-06, 1.985798503595697e-06, 1.4951894615308778e-06, 1.0513050901388984e-06, 8.877687427839586e-07, 4.438843713919793e-07, 5.606960480740792e-07, 3.971597007191394e-07, 3.504350300462995e-07, 1.8689868269135972e-07, 2.803480240370396e-07, 2.1026101802777968e-07, 2.1026101802777968e-07, 1.6353634735493976e-07, 7.00870060092599e-08, 1.6353634735493976e-07, 1.8689868269135972e-07, 9.344934134567986e-08, 9.344934134567986e-08, 9.344934134567986e-08, 7.00870060092599e-08, 9.344934134567986e-08, 1.1681167668209982e-07, 9.344934134567986e-08, 1.1681167668209982e-07, 1.401740120185198e-07, 7.00870060092599e-08, 4.672467067283993e-08, 9.344934134567986e-08, 2.3362335336419965e-08, 4.672467067283993e-08, 4.672467067283993e-08, 4.672467067283993e-08, 4.672467067283993e-08, 2.3362335336419965e-08, 9.344934134567986e-08, 2.3362335336419965e-08, 2.3362335336419965e-08, 9.344934134567986e-08, 2.3362335336419965e-08, 2.3362335336419965e-08, 4.672467067283993e-08, 2.3362335336419965e-08, 7.00870060092599e-08, 4.672467067283993e-08, 2.3362335336419965e-08, 2.3362335336419965e-08, 2.3362335336419965e-08, 4.672467067283993e-08, 2.3362335336419965e-08, 4.672467067283993e-08, 2.3362335336419965e-08, 2.3362335336419965e-08, 2.3362335336419965e-08, 4.672467067283993e-08, 2.3362335336419965e-08, 2.3362335336419965e-08, 2.3362335336419965e-08, 2.3362335336419965e-08, 4.672467067283993e-08, 2.3362335336419965e-08, 4.672467067283993e-08, 2.3362335336419965e-08, 2.3362335336419965e-08, 4.672467067283993e-08, 2.3362335336419965e-08, 4.672467067283993e-08, 2.3362335336419965e-08, 2.3362335336419965e-08, 2.3362335336419965e-08, 4.672467067283993e-08, 4.672467067283993e-08, 2.3362335336419965e-08, 7.00870060092599e-08, 2.3362335336419965e-08, 2.3362335336419965e-08, 2.3362335336419965e-08, 2.3362335336419965e-08, 2.3362335336419965e-08]\n" + ] + }, + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Plotting\n", + "fig, ax1 = plt.subplots()\n", + "\n", + "# Plot sc_pairs on the first y-axis\n", + "ax1.scatter(seg_len_filtered, avg_range_filtered, label='avg_range', color='blue')\n", + "ax1.set_xlabel('seg_len')\n", + "ax1.set_ylabel('avg_range', color='blue')\n", + "ax1.tick_params(axis='y', labelcolor='blue')\n", + "ax1.set_ylim(0, 600)\n", + "ax1.set_xlim(0, 10000)\n", + "# ax1.set_xscale('log') # Set logarithmic scale for avg_range\n", + "\n", + "print(\"percentage of anchors\")\n", + "anchors_filtered = [x / sum(anchors_filtered) for x in anchors_filtered]\n", + "print(avg_range_filtered)\n", + "\n", + "# # Plot sc_pairs on the first y-axis\n", + "# ax1.plot(seg_len, sc_pairs, label='sc_pairs', color='blue')\n", + "# ax1.set_xlabel('seg_len')\n", + "# ax1.set_ylabel('sc_pairs', color='blue')\n", + "# ax1.tick_params(axis='y', labelcolor='blue')\n", + "\n", + "# Create a second y-axis for anchors\n", + "# ax2 = ax1.twinx()\n", + "# ax2.plot(seg_len_filtered, anchors_filtered, label='anchors', color='green')\n", + "# ax2.set_ylabel('anchors', color='green')\n", + "# ax2.tick_params(axis='y', labelcolor='green')\n", + "# ax2.set_yscale('log') # Set logarithmic scale for segs\n", + "# ax2.set_ylim(0.00000001, 5)\n", + "\n", + "# Create a third y-axis for segs\n", + "ax3 = ax1.twinx()\n", + "ax3.plot(seg_len_filtered, segs_filtered, label='segs', color='red')\n", + "ax3.set_ylabel('segs', color='red')\n", + "ax3.tick_params(axis='y', labelcolor='red')\n", + "ax3.set_yscale('log') # Set logarithmic scale for segs\n", + "ax3.set_ylim(0.00000001, 5)\n", + "print(segs_filtered)\n", + "# print([x / sum(segs_filtered) for x in segs_filtered])\n", + "\n", + "# Add legend\n", + "lines = ax1.get_lines() + ax3.get_lines()\n", + "ax1.legend(lines, [line.get_label() for line in lines], loc='upper left')\n", + "\n", + "# Add horizontal lines\n", + "line_64 = ax1.axhline(y=64, color='c', linestyle='--')\n", + "line_1024 = ax1.axhline(y=1024, color='c', linestyle='--')\n", + "# # Calculate midpoints of horizontal lines\n", + "# midpoint_64 = (line_64.get_ydata()[0] + line_64.get_ydata()[1]) / 2\n", + "# midpoint_1024 = (line_1024.get_ydata()[0] + line_1024.get_ydata()[1]) / 2\n", + "# # # Add labels at midpoints\n", + "# ax1.text(0, midpoint_64, 'avg_range = 64', ha='right', va='center', color='c')\n", + "# ax1.text(0, midpoint_1024, 'avg_range = 1024', ha='right', va='center', color='c')\n", + "\n", + "plt.title('avg_range and number of segs with respect to seg_len')\n", + "plt.grid(True)\n", + "plt.show()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.18" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/scripts/mbit10_integrated.sh b/scripts/mbit10_integrated.sh new file mode 100755 index 00000000..7700e50c --- /dev/null +++ b/scripts/mbit10_integrated.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +make clean +make MICRO_BATCH=4 GPU=NV GPU_CONFIG=a6000_config.json SHORT_BLOCK_SIZE=32 LONG_BLOCK_SIZE=1024 MID_BLOCK_SIZE=512 MID_CUT=1 LONG_CUT=100 DEBUG_ANALYSIS=1 +./minimap2 -t 1 --max-chain-skip=2147483647 --gpu-chain data/hg38.mmi data/random_500MBases_90kto100k.fa \ No newline at end of file diff --git a/scripts/ncu_all.sh b/scripts/ncu_all.sh new file mode 100644 index 00000000..5c3ddb00 --- /dev/null +++ b/scripts/ncu_all.sh @@ -0,0 +1,37 @@ +#!/bin/bash + +# Array of LONG_BLOCK_SIZE values +MID_BLOCK_SIZES=( 512 256 ) +MID_CUTS=( 1 ) +LONG_CUTS=( 50 100 ) +GPU_CONFIGS=( gpu_config1.json ) +DATA_SETS=( 1kto300k ) + +# Iterate over LONG_BLOCK_SIZES array +for DATA_SET in "${DATA_SETS[@]}" +do + for MID_BLOCK_SIZE in "${MID_BLOCK_SIZES[@]}" + do + for MID_CUT in "${MID_CUTS[@]}" + do + for LONG_CUT in "${LONG_CUTS[@]}" + do + for GPU_CONFIG in "${GPU_CONFIGS[@]}" + do + echo "Executing with MID_BLOCK_SIZE=${MID_BLOCK_SIZE} MID_CUT=${MID_CUT} LONG_CUT=${LONG_CUT}" + + # Clean the project + make GPU=NVCC clean + + # Build with specific configurations + make GPU=NVCC GPU_CONFIG=${GPU_CONFIG} SHORT_BLOCK_SIZE=64 MID_BLOCK_SIZE=${MID_BLOCK_SIZE} LONG_BLOCK_SIZE=512 MID_CUT=${MID_CUT} LONG_CUT=${LONG_CUT} + + # # Run nsys ncu with the given command # data/random_500MBases_200kto300k.fa + # read by `ncu --import ncu_midXXX_X_XXX_gpu_configX.json_XXXktoXXXk_report.ncu-rep --section SpeedOfLight --kernel-name score_generation_mid -c 1 --csv > test.csv` + ncu --export "ncu/ncu32_mid${MID_BLOCK_SIZE}_${MID_CUT}_${LONG_CUT}_${GPU_CONFIG}_${DATA_SET}_report" --force-overwrite --target-processes all --kernel-name-base function --kernel-name regex:score_generation_mid --launch-count 1 --launch-skip-before-match 0 --section ComputeWorkloadAnalysis --section InstructionStats --section LaunchStats --section MemoryWorkloadAnalysis --section MemoryWorkloadAnalysis_Chart --section MemoryWorkloadAnalysis_Tables --section Occupancy --section SchedulerStats --section SourceCounters --section SpeedOfLight --section SpeedOfLight_RooflineChart --section WarpStateStats --sampling-interval auto --sampling-max-passes 5 --sampling-buffer-size 33554432 --profile-from-start 1 --cache-control all --clock-control base --apply-rules yes --import-source yes --source-folders gpu --check-exit-code yes ./minimap2 data/hg38.mmi data/random_500MBases_${DATA_SET}.fa -t 1 --max-chain-skip=2147483647 --gpu-chain # 2> ncu/runtime_report_mid${MID_BLOCK_SIZE}_${MID_CUT}_${LONG_CUT}_${DATA_SET}.txt + echo "Done with MID_BLOCK_SIZE=${MID_BLOCK_SIZE} MID_CUT=${MID_CUT} LONG_CUT=${LONG_CUT}" + done + done + done + done +done \ No newline at end of file diff --git a/scripts/ncu_integrated.sh b/scripts/ncu_integrated.sh new file mode 100644 index 00000000..2906989b --- /dev/null +++ b/scripts/ncu_integrated.sh @@ -0,0 +1 @@ +ncu --export "ncu/report" --force-overwrite --target-processes all --kernel-name-base function --kernel-name score_generation_long --launch-skip-before-match 0 --section ComputeWorkloadAnalysis --section InstructionStats --section LaunchStats --section MemoryWorkloadAnalysis --section MemoryWorkloadAnalysis_Chart --section MemoryWorkloadAnalysis_Tables --section Occupancy --section SchedulerStats --section SourceCounters --section SpeedOfLight --section SpeedOfLight_RooflineChart --section WarpStateStats --sampling-interval auto --sampling-max-passes 5 --sampling-buffer-size 33554432 --profile-from-start 1 --cache-control all --clock-control base --apply-rules yes --import-source yes --source-folders gpu --check-exit-code yes --replay-mode application ./minimap2 data/hg38.mmi data/random_100MBases_1kto300k.fa -t 4 --max-chain-skip=2147483647 --gpu-chain diff --git a/scripts/nvprof_all.sh b/scripts/nvprof_all.sh new file mode 100644 index 00000000..9c5ed4a9 --- /dev/null +++ b/scripts/nvprof_all.sh @@ -0,0 +1,30 @@ +#!/bin/bash + +# Array of LONG_BLOCK_SIZE values +MID_BLOCK_SIZES=( 128 256 512 768 1024 ) +MID_CUTS=( 1 ) +LONG_CUTS=( 50 60 70 ) + +# Iterate over LONG_BLOCK_SIZES array +for MID_BLOCK_SIZE in "${MID_BLOCK_SIZES[@]}" +do + for MID_CUT in "${MID_CUTS[@]}" + do + for LONG_CUT in "${LONG_CUTS[@]}" + do + echo "Executing with MID_BLOCK_SIZE=${MID_BLOCK_SIZE} MID_CUT=${MID_CUT} LONG_CUT=${LONG_CUT}" + + # Clean the project + make clean + + # Build with specific configurations + make GPU=NVCC GPU_CONFIG=gpu_config.json SHORT_BLOCK_SIZE=64 MID_BLOCK_SIZE=${MID_BLOCK_SIZE} LONG_BLOCK_SIZE=1024 MID_CUT=${MID_CUT} LONG_CUT=${LONG_CUT} + + # # Run nsys nvprof with the given command # data/random_500MBases_200kto300k.fa + # read by `nsys stats --report cuda_gpu_kern_sum nsys/nvprofxxx_xxx_xxx.nsys-rep` + nsys nvprof -o nsys/nvprof_mid${MID_BLOCK_SIZE}_${MID_CUT}_${LONG_CUT} ./minimap2 data/hg38.mmi data/random_500MBases_200kto300k.fa -t 1 --max-chain-skip=2147483647 --gpu-chain 2> nsys/runtime_report_mid${MID_BLOCK_SIZE}_${MID_CUT}_${LONG_CUT}.txt + # nsys stats --format csv --report cuda_gpu_kern_sum nsys/nvprofxxx_xxx_xxx.nsys-rep + echo "Done with MID_BLOCK_SIZE=${MID_BLOCK_SIZE} MID_CUT=${MID_CUT} LONG_CUT=${LONG_CUT}" + done + done +done diff --git a/scripts/parse_seg.py b/scripts/parse_seg.py new file mode 100644 index 00000000..59268361 --- /dev/null +++ b/scripts/parse_seg.py @@ -0,0 +1,66 @@ +import re +import argparse +import matplotlib.pyplot as plt + +# Define a function to extract segments from a line +def extract_segments(line): + match = re.search(r'long segments (\d+)', line) + if match: + if int(match.group(1)) > 1000000: + print(line) + return int(match.group(1)) + return None + +# Define a function to extract runtime from a line +def extract_runtime(line): + match = re.search(r'last launch runtime: (\d+\.\d+) ms', line) + if match: + return float(match.group(1)) + return None + + +# Initialize variables to store segment counts +segment_counts = [] +runtimes = [] + +# Create an argument parser to get the output file name from the command line +parser = argparse.ArgumentParser(description='Compute and plot a histogram of segments from an output file.') +parser.add_argument('output_file', help='Path to the output file containing segment data') +parser.add_argument('runtime_file', help='Path to the file containing runtime data') +args = parser.parse_args() + +# Read the output file specified in the command line argument +with open(args.output_file, 'r') as file: + for line in file: + segments = extract_segments(line) + if segments is None: + continue + segment_counts.append(segments) + + +# Read the runtime file specified in the command line argument +with open(args.runtime_file, 'r') as runtime_file: + for line in runtime_file: + runtime = extract_runtime(line) + if runtime is None: + continue + runtimes.append(runtime) + +# Calculate the total number of segments +total_segments = sum(segment_counts) +total_runtime = sum(runtimes) +throughput = total_segments / total_runtime # anchors/ms + +# Create a histogram +plt.hist(segment_counts, bins=200, edgecolor='k') +plt.xlabel('Segments') +plt.ylabel('Frequency') +plt.title(f'Total Segments: {total_segments}, throughput: {throughput} anchors/ms') +plt.grid(True) + +# Save the figure with an appropriate name based on the input file name +output_filename = args.output_file #.split('.')[0] # Remove the file extension +plt.savefig(f'{output_filename}_segment_histogram.png') + +# Display the histogram +# plt.show() diff --git a/scripts/run_ncu_profile.sh b/scripts/run_ncu_profile.sh new file mode 100644 index 00000000..66caf30d --- /dev/null +++ b/scripts/run_ncu_profile.sh @@ -0,0 +1 @@ +ncu --export "ncu/report" --force-overwrite --target-processes all --kernel-name-base function --launch-skip-before-match 0 --section ComputeWorkloadAnalysis --section InstructionStats --section LaunchStats --section MemoryWorkloadAnalysis --section MemoryWorkloadAnalysis_Chart --section MemoryWorkloadAnalysis_Tables --section Occupancy --section SchedulerStats --section SourceCounters --section SpeedOfLight --section SpeedOfLight_RooflineChart --section WarpStateStats --sampling-interval auto --sampling-max-passes 5 --sampling-buffer-size 33554432 --profile-from-start 1 --cache-control all --clock-control base --apply-rules yes --import-source yes --source-folders gpu --check-exit-code yes ./minimap2 data/hg38.mmi data/tiny.fa -t 1 --max-chain-skip=2147483647 --gpu-chain \ No newline at end of file diff --git a/scripts/test_index.cpp b/scripts/test_index.cpp new file mode 100644 index 00000000..e959122c --- /dev/null +++ b/scripts/test_index.cpp @@ -0,0 +1,80 @@ +#include + + +int main(int argc, const char** argv) { + + unsigned blockdim = atoi(argv[1]); + unsigned range = atoi(argv[2]); + unsigned i_range = atoi(argv[3]); + unsigned buffer_size = atoi(argv[4]); + + unsigned start_idx = 3; + + int anchor_offset[blockdim] = {0}; + for (unsigned tid = 0; tid < blockdim; ++tid) { + anchor_offset[tid] = tid + (start_idx - tid + blockdim) / blockdim * blockdim; + std::cout << "tid: " << tid << " " << anchor_offset[tid] << std::endl; + } + + // unsigned anchors[blockdim][buffer_size] = {0}; + // unsigned i = 0; + // for (unsigned tid = 0; tid < blockdim; ++tid) { + // // update when drop becomes 0 + // // TODO: think about how to update the array of anchors + // for (unsigned j = tid+1, index=0; j < i+range+1; j += blockdim, index++) { + // anchors[tid][index] = j; + // } + // } + // // print the anchors + // for (unsigned tid = 0; tid < blockdim; ++tid) { + // std::cout << "tid: " << tid << " ["; + // for (unsigned j = 0; j < buffer_size; ++j) { + // std::cout << anchors[tid][j] << " "; + // } + // std::cout << "]" << std::endl; + // } + + for (unsigned i = start_idx; i < i_range; ++i) { + std::cout << "anchor i: " << i << std::endl; + for (unsigned tid = 0; tid < blockdim; ++tid) { + std::cout << "tid: " << tid << ": "; + for (unsigned j = anchor_offset[tid]; j < i+range+1; j += blockdim) { + std::cout << j << " "; + } + if (anchor_offset[tid] <= i+1) { + anchor_offset[tid] += blockdim; + } + std::cout << std::endl; + // unsigned drop = (i+blockdim-tid-1) % blockdim; + // unsigned offset = (i+blockdim-tid-1) / blockdim; + // // remove old when drop becomes 0 + // // add new when drop becomes (blockdim - range%blockdim) + // // TODO: think about how to update the array of anchors efficiently + // // easiest, generate a mask with ballot and use cooperative groups + // std::cout << "tid: " << tid << " offset: " << offset << " drop: " << drop << " ("; + // if (!drop) { + // anchors[tid][(offset+buffer_size-1)%buffer_size] = 0; + // } + // for (unsigned j = offset*blockdim+tid+1, index=0; j < i+range+1; j += blockdim, index++) { + // if (drop == blockdim - range%blockdim && index == buffer_size-1) { + // anchors[tid][(index+offset)%buffer_size] = j; + // } + // std::cout << j << " : " << anchors[tid][(index+offset)%buffer_size] << ", "; + // // anchors[tid][index] = j; + // } + // std::cout << ") " << std::endl; + } + std::cout << std::endl; + // for (unsigned tid = 0; tid < blockdim; ++tid) { + // std::cout << "tid: " << tid << " ["; + // for (unsigned j = 0; j < buffer_size; ++j) { + // std::cout << anchors[tid][j] << " "; + // } + // std::cout << "]" << std::endl; + // } + + } + + + return 0; +}