083833e1f84528f923fde137a041657635f4564e hiram Thu Jul 20 22:29:00 2017 -0700 remove the CPU cache experiment and change memLimit option to threads refs #18969 diff --git src/utils/crisprKmers/crisprKmers.c src/utils/crisprKmers/crisprKmers.c index 0f6a72c..a69e768 100644 --- src/utils/crisprKmers/crisprKmers.c +++ src/utils/crisprKmers/crisprKmers.c @@ -66,60 +66,54 @@ /* Explain usage and exit. */ { errAbort( "crisprKmers - annotate crispr guide sequences\n" "usage:\n" " crisprKmers \n" "options:\n" " where is a .2bit file or .fa fasta sequence\n" " -verbose=N - for debugging control processing steps with level of verbose:\n" " - verbose < 2 - scan sequence only, no comparisons\n" " - verbose > 1 - run comparisons\n" " -bed= - output results to given bed9+ file\n" " -offTargets= - output off target data to given file\n" " -ranges= - use specified bed3 file to limit which guides are\n" " - measured, only those with any overlap to these bed items.\n" - " -memLimit=N - N number of gigabytes for each thread, default: no threading\n" - " when memory size goes beyond N gB program will thread gB/N times\n" - " -cacheCount=N - number of guides to check at one time vs. targets,\n" - " - to take advantage of CPU cache, e.g. 1Mb of cache\n" - " - could hold 16,384 64bit words.\n" + " -threads=N - N number of threads to run, default: no threading\n" + " - use when ku is going to allocate more CPUs for big mem jobs.\n" " -dumpKmers= - NOT VALID after scan of sequence, output kmers to file\n" " - process will exit after this, use -loadKmers to continue\n" " -loadKmers= - NOT VALID load kmers from previous scan of sequence from -dumpKmers" ); } static char *bedFileOut = NULL; /* set with -bed= argument, kmer output */ static char *offTargets = NULL; /* write offTargets to */ static FILE *offFile = NULL; /* file handle to write off targets */ static char *ranges = NULL; /* use ranges to limit scanning */ static struct hash *rangesHash = NULL; /* ranges into hash + binkeeper */ static char *dumpKmers = NULL; /* file name to write out kmers from scan */ static char *loadKmers = NULL; /* file name to read in kmers from previous scan */ -static int memLimit = 0; /* gB limit before going into thread mode */ -static int cacheCount = 0; /* number of query guides to run through - * for each target to utilize CPU cache */ +static int threads = 0; /* number of threads to run */ static int threadCount = 1; /* will be adjusted depending upon vmPeak */ /* Command line validation table. */ static struct optionSpec options[] = { {"bed", OPTION_STRING}, {"offTargets", OPTION_STRING}, {"ranges", OPTION_STRING}, - {"memLimit", OPTION_INT}, - {"cacheCount", OPTION_INT}, + {"threads", OPTION_INT}, {"dumpKmers", OPTION_STRING}, {"loadKmers", OPTION_STRING}, {NULL, 0}, }; #define guideSize 20 // 20 bases #define pamSize 3 // 3 bases #define negativeStrand 0x0000800000000000 #define fortyEightBits 0xffffffffffff #define fortySixBits 0x3fffffffffff #define fortyBits 0xffffffffff #define high32bits 0xffffffff00000000 #define low32bits 0x00000000ffffffff #define high16bits 0xffff0000ffff0000 @@ -518,31 +512,34 @@ char pamString[33]; kmerPAMString(pamString, list->sequence[c]); if (bedFH) { long mitScore = 1.0; if (mitScoreCount > 0) /* note: interger <- float conversion */ { mitScore = roundf((list->mitSum[c] / (float) mitScoreCount)); } else if (list->offBy[0][c] > 0) { mitScore = 0.0; } char *color = scoreToColor(mitScore); - fprintf(bedFH, "%s\t%lld\t%lld\t\t%ld\t%c\t%lld\t%lld\t%s\t%s\t%s\t%ld\t%d,%d,%d,%d,%d\n", list->chrom, list->start[c], list->start[c]+pamSize+guideSize, mitScore, strand, txStart, txEnd, color, kmerString, pamString, mitScore, list->offBy[0][c], list->offBy[1][c], list->offBy[2][c], list->offBy[3][c], list->offBy[4][c]); + /* the end zero will be filled in by post-processing, it will be + * the _offset into the crisprDetails.tab file + */ + fprintf(bedFH, "%s\t%lld\t%lld\t\t%ld\t%c\t%lld\t%lld\t%s\t%s\t%s\t%d,%d,%d,%d,%d\t0\n", list->chrom, list->start[c], list->start[c]+pamSize+guideSize, mitScore, strand, txStart, txEnd, color, kmerString, pamString, list->offBy[0][c], list->offBy[1][c], list->offBy[2][c], list->offBy[3][c], list->offBy[4][c]); } ++totalOut; } } if (bedFH) carefulClose(&bedFH); timingMessage("countsOutput:", itemsOutput, "items output", startTime, "items/sec", "seconds/item"); } // static void countsOutput(struct crisprList *all, FILE *bedFH) static struct crisprList *scanSequence(char *inFile) /* scan the given file, return list of crisprs */ { @@ -976,33 +973,31 @@ query->chrom, query->start[qIndex], negativeStrand & query->sequence[qIndex] ? '-' : '+', queryString, queryPAM, targetString, targetPAM, target->chrom, target->start[tIndex], negativeStrand & target->sequence[tIndex] ? '-' : '+', cfdInt, misMatch, bitsOn, mitScore, cfdScore, target->chrom, target->start[tIndex], negativeStrand & target->sequence[tIndex] ? '-' : '+'); } } } // static void recordOffTargets(struct crisprList *query, // struct crisprList *target, int bitsOn, long long qIndex, // long long tIndex) -/* this queryVsTarget can be used by threads, it should be safe, - * remains to be proven. - */ +/* this queryVsTarget can be used by threads, appears to be safe */ static void queryVsTarget(struct crisprList *query, struct crisprList *target, int threadCount, int threadId) /* run the query crisprs list against the target list in the array structures */ { struct crisprList *qList; long long totalCrisprsQuery = 0; long long totalCrisprsTarget = 0; long long totalCompares = 0; struct loopControl *control = NULL; AllocVar(control); long startTime = clock1000(); for (qList = query; qList; qList = qList->next) { @@ -1011,77 +1006,65 @@ { setLoopEnds(control, qList->crisprCount, threadCount, threadId); if (control->listStart >= qList->crisprCount) continue; /* next chrom, no part to be done for this thread */ } else { control->listStart = 0; control->listEnd = qList->crisprCount; } verbose(1, "# thread %d of %d running %s %lld items [ %lld : %lld )\n", 1 + threadId, threadCount, qList->chrom, control->listEnd - control->listStart, control->listStart, control->listEnd); totalCrisprsTarget += control->listEnd - control->listStart; - long long qStart = control->listStart; - long long qEnd = control->listEnd; long long qCount; - for (qCount = control->listStart; qCount < control->listEnd; qCount = qEnd) - { - qStart = qCount; - qEnd = qStart + cacheCount; - if (cacheCount < 1) - qEnd = qStart + 1; - if (qEnd > control->listEnd) - qEnd = control->listEnd; + for (qCount = control->listStart; qCount < control->listEnd; ++qCount) + { struct crisprList *tList; for (tList = target; tList; tList = tList->next) { long long tCount; totalCompares += tList->crisprCount; for (tCount = 0; tCount < tList->crisprCount; ++tCount) { - long long q; - for (q = qStart; q < qEnd; ++q) - { /* the XOR determine differences in two sequences, the * shift right 6 removes the PAM sequence and * the 'fortyBits &' eliminates the negativeStrand bit */ long long misMatch = fortyBits & - ((qList->sequence[q] ^ tList->sequence[tCount]) >> 6); + ((qList->sequence[qCount] ^ tList->sequence[tCount]) >> 6); if (misMatch) { /* possible misMatch bit values: 01 10 11 * turn those three values into just: 01 */ misMatch = (misMatch | (misMatch >> 1)) & 0x5555555555; int bitsOn = _mm_popcnt_u64(misMatch); if (bitsOn < 5) { - recordOffTargets(qList, tList, bitsOn, q, tCount, misMatch); - qList->offBy[bitsOn][q] += 1; + recordOffTargets(qList, tList, bitsOn, qCount, tCount, misMatch); + qList->offBy[bitsOn][qCount] += 1; // tList->offBy[bitsOn][tCount] += 1; not needed } } else { /* no misMatch, identical crisprs */ - qList->offBy[0][q] += 1; + qList->offBy[0][qCount] += 1; // tList->offBy[0][tCount] += 1; not needed } - } // for (q = qStart; q < qEnd; ++q) } // for (tCount = 0; tCount < tList->crisprCount; ++tCount) } // for (tList = target; tList; tList = tList->next) } // for (qCount = 0; qCount < qList->crisprCount; ++qCount) } // for (qList = query; qList; qList = qList->next) timingMessage("queryVsTarget", totalCrisprsQuery, "query crisprs processed", startTime, "crisprs/sec", "seconds/crispr"); timingMessage("queryVsTarget", totalCrisprsTarget, "vs target crisprs", startTime, "crisprs/sec", "seconds/crispr"); timingMessage("queryVsTarget", totalCompares, "total comparisons", startTime, "compares/sec", "seconds/compare"); } /* static struct crisprList *queryVsTarget(struct crisprList *query, struct crisprList *target) */ @@ -1304,34 +1287,35 @@ offFile = mustOpen(offTargets, "w"); /* if ranges have been specified, construct queryList of kmers to measure */ if (rangesHash) { /* result here is two exclusive sets: query, and allGuides * the query crisprs have been extracted from the allGuides */ queryGuides = rangeExtraction(& allGuides); } if (queryGuides) copyToArray(queryGuides); if (allGuides) copyToArray(allGuides); vmPeak = currentVmPeak(); verbose(1, "# vmPeak after copyToArray: %lld kB\n", vmPeak); /* larger example: 62646196 kB */ - if ((memLimit > 0) && ((vmPeak >> 20) > memLimit)) // the >> 20 converts kB to gB + if (threads > 1) { - threadCount = 1 + ((vmPeak >> 20) / memLimit); - verbose(1, "# over %d Gb at %lld kB, threadCount: %d\n", memLimit, vmPeak, threadCount); + int gB = vmPeak >> 20; /* convert kB to Gb */ + threadCount = threads; + verbose(1, "# at %d Gb (%lld kB), running %d threads\n", gB, vmPeak, threadCount); } if (queryGuides) // when range selected some query sequences { if (allGuides) // if there are any left on the all list { if (threadCount > 1) runThreads(threadCount, queryGuides, allGuides); else queryVsTarget(queryGuides, allGuides, 1, 0); } /* then run up the query vs. itself avoiding self vs. self */ queryVsSelf(queryGuides); countsOutput(queryGuides, bedFH); } else @@ -1345,40 +1329,35 @@ } // static void crisprKmers(char *sequence, FILE *bedFH) int main(int argc, char *argv[]) /* Process command line, initialize translation arrays and call the process */ { optionInit(&argc, argv, options); if (argc != 2) usage(); verbose(0, "# running verboseLevel: %d\n", verboseLevel()); bedFileOut = optionVal("bed", bedFileOut); dumpKmers = optionVal("dumpKmers", dumpKmers); loadKmers = optionVal("loadKmers", loadKmers); offTargets = optionVal("offTargets", offTargets); -memLimit = optionInt("memLimit", memLimit); -if (memLimit < 0) - errAbort("specified memLimi is less than 0: %d\n", memLimit); -if (memLimit > 0) - verbose(1, "# memLimit %d gB\n", memLimit); -cacheCount = optionInt("cacheCount", cacheCount); -if (cacheCount < 0) - errAbort("specified cacheCount is less than 0: %d\n", cacheCount); -if (cacheCount > 0) - verbose(1, "# cacheCount %d items\n", cacheCount); +threads = optionInt("threads", threads); +if (threads < 0) + errAbort("specified threads is less than 0: %d\n", threads); +if (threads > 0) + verbose(1, "# requesting threads: %d\n", threads); ranges = optionVal("ranges", ranges); if (ranges) rangesHash = readRanges(ranges); FILE *bedFH = NULL; if (bedFileOut) { bedFH = mustOpen(bedFileOut, "w"); verbose(1, "# output to bed file: %s\n", bedFileOut); } initOrderedNtVal(); /* set up orderedNtVal[] */ crisprKmers(argv[1], bedFH); if (verboseLevel() > 1)