static int thread_loop(ESL_THREADS *obj, ESL_WORK_QUEUE *queue, P7_HMMFILE *hfp) { int status = eslOK; int sstatus = eslOK; int eofCount = 0; P7_OM_BLOCK *block; ESL_ALPHABET *abc = NULL; void *newBlock; esl_workqueue_Reset(queue); esl_threads_WaitForStart(obj); status = esl_workqueue_ReaderUpdate(queue, NULL, &newBlock); if (status != eslOK) esl_fatal("Work queue reader failed"); /* Main loop: */ while (sstatus == eslOK) { block = (P7_OM_BLOCK *) newBlock; sstatus = p7_oprofile_ReadBlockMSV(hfp, &abc, block); if (sstatus == eslEOF) { if (eofCount < esl_threads_GetWorkerCount(obj)) sstatus = eslOK; ++eofCount; } if (sstatus == eslOK) { status = esl_workqueue_ReaderUpdate(queue, block, &newBlock); if (status != eslOK) esl_fatal("Work queue reader failed"); } } status = esl_workqueue_ReaderUpdate(queue, block, NULL); if (status != eslOK) esl_fatal("Work queue reader failed"); if (sstatus == eslEOF) { /* wait for all the threads to complete */ esl_threads_WaitForFinish(obj); esl_workqueue_Complete(queue); } esl_alphabet_Destroy(abc); return sstatus; }
static void process_SearchCmd(HMMD_COMMAND *cmd, WORKER_ENV *env) { int i; int cnt; int limit; int status; int blk_size; WORKER_INFO *info = NULL; ESL_ALPHABET *abc; ESL_STOPWATCH *w; ESL_THREADS *threadObj = NULL; pthread_mutex_t inx_mutex; int current_index; QUEUE_DATA *query = NULL; time_t date; char timestamp[32]; w = esl_stopwatch_Create(); abc = esl_alphabet_Create(eslAMINO); if (pthread_mutex_init(&inx_mutex, NULL) != 0) p7_Fail("mutex init failed"); ESL_ALLOC(info, sizeof(*info) * env->ncpus); /* Log the current time (at search start) */ date = time(NULL); ctime_r(&date, timestamp); printf("\n%s", timestamp); /* note that ctime_r() leaves \n on end of timestamp */ /* initialize thread data */ query = process_QueryCmd(cmd, env); esl_stopwatch_Start(w); info->range_list = NULL; if (esl_opt_IsUsed(query->opts, "--seqdb_ranges")) { ESL_ALLOC(info->range_list, sizeof(RANGE_LIST)); hmmpgmd_GetRanges(info->range_list, esl_opt_GetString(query->opts, "--seqdb_ranges")); } if (query->cmd_type == HMMD_CMD_SEARCH) threadObj = esl_threads_Create(&search_thread); else threadObj = esl_threads_Create(&scan_thread); if (query->query_type == HMMD_SEQUENCE) { fprintf(stdout, "Search seq %s [L=%ld]", query->seq->name, (long) query->seq->n); } else { fprintf(stdout, "Search hmm %s [M=%d]", query->hmm->name, query->hmm->M); } fprintf(stdout, " vs %s DB %d [%d - %d]", (query->cmd_type == HMMD_CMD_SEARCH) ? "SEQ" : "HMM", query->dbx, query->inx, query->inx + query->cnt - 1); if (info->range_list) fprintf(stdout, " in range(s) %s", esl_opt_GetString(query->opts, "--seqdb_ranges")); fprintf(stdout, "\n"); /* Create processing pipeline and hit list */ for (i = 0; i < env->ncpus; ++i) { info[i].abc = query->abc; info[i].hmm = query->hmm; info[i].seq = query->seq; info[i].opts = query->opts; info[i].range_list = info[0].range_list; info[i].th = NULL; info[i].pli = NULL; info[i].inx_mutex = &inx_mutex; info[i].inx = ¤t_index;/* this is confusing trickery - to share a single variable across all threads */ info[i].blk_size = &blk_size; /* ditto */ info[i].limit = &limit; /* ditto. TODO: come back and clean this up. */ if (query->cmd_type == HMMD_CMD_SEARCH) { HMMER_SEQ **list = env->seq_db->db[query->dbx].list; info[i].sq_list = &list[query->inx]; info[i].sq_cnt = query->cnt; info[i].db_Z = env->seq_db->db[query->dbx].K; info[i].om_list = NULL; info[i].om_cnt = 0; } else { info[i].sq_list = NULL; info[i].sq_cnt = 0; info[i].db_Z = 0; info[i].om_list = &env->hmm_db->list[query->inx]; info[i].om_cnt = query->cnt; } esl_threads_AddThread(threadObj, &info[i]); } /* try block size of 5000. we will need enough sequences for four * blocks per thread or better. */ blk_size = 5000; cnt = query->cnt / env->ncpus / blk_size; limit = query->cnt * 2 / 3; if (cnt < 4) { /* try block size of 1000 */ blk_size /= 5; cnt = query->cnt / env->ncpus / blk_size; if (cnt < 4) { /* still not enough. just divide it up into one block per thread */ blk_size = query->cnt / env->ncpus + 1; limit = query->cnt * 2; } } current_index = 0; esl_threads_WaitForStart(threadObj); esl_threads_WaitForFinish(threadObj); esl_stopwatch_Stop(w); #if 1 fprintf (stdout, " Sequences Residues Elapsed\n"); for (i = 0; i < env->ncpus; ++i) { print_timings(i, info[i].elapsed, info[i].pli); } #endif /* merge the results of the search results */ for (i = 1; i < env->ncpus; ++i) { p7_tophits_Merge(info[0].th, info[i].th); p7_pipeline_Merge(info[0].pli, info[i].pli); p7_pipeline_Destroy(info[i].pli); p7_tophits_Destroy(info[i].th); } print_timings(99, w->elapsed, info[0].pli); send_results(env->fd, w, info); /* free the last of the pipeline data */ p7_pipeline_Destroy(info->pli); p7_tophits_Destroy(info->th); free_QueueData(query); esl_threads_Destroy(threadObj); pthread_mutex_destroy(&inx_mutex); if (info->range_list) { if (info->range_list->starts) free(info->range_list->starts); if (info->range_list->ends) free(info->range_list->ends); free (info->range_list); } free(info); esl_stopwatch_Destroy(w); esl_alphabet_Destroy(abc); return; ERROR: LOG_FATAL_MSG("malloc", errno); }
static int thread_loop(ESL_THREADS *obj, ESL_WORK_QUEUE *queue, struct cfg_s *cfg) { int status = eslOK; int sstatus = eslOK; int processed = 0; WORK_ITEM *item; void *newItem; int next = 1; PENDING_ITEM *top = NULL; PENDING_ITEM *empty = NULL; PENDING_ITEM *tmp = NULL; char errmsg[eslERRBUFSIZE]; esl_workqueue_Reset(queue); esl_threads_WaitForStart(obj); status = esl_workqueue_ReaderUpdate(queue, NULL, &newItem); if (status != eslOK) esl_fatal("Work queue reader failed"); /* Main loop: */ item = (WORK_ITEM *) newItem; while (sstatus == eslOK) { sstatus = esl_msa_Read(cfg->afp, &item->msa); if (sstatus == eslOK) { item->nali = ++cfg->nali; if (set_msa_name(cfg, errmsg, item->msa) != eslOK) p7_Fail("%s\n", errmsg); } if (sstatus == eslEOF && processed < cfg->nali) sstatus = eslOK; if (sstatus == eslOK) { status = esl_workqueue_ReaderUpdate(queue, item, &newItem); if (status != eslOK) esl_fatal("Work queue reader failed"); /* process any results */ item = (WORK_ITEM *) newItem; if (item->processed == TRUE) { ++processed; /* try to keep the input output order the same */ if (item->nali == next) { sstatus = output_result(cfg, errmsg, item->nali, item->msa, item->hmm, item->postmsa, item->entropy); if (sstatus != eslOK) p7_Fail(errmsg); p7_hmm_Destroy(item->hmm); esl_msa_Destroy(item->msa); esl_msa_Destroy(item->postmsa); ++next; /* output any pending msa as long as the order * remains the same as read in. */ while (top != NULL && top->nali == next) { sstatus = output_result(cfg, errmsg, top->nali, top->msa, top->hmm, top->postmsa, top->entropy); if (sstatus != eslOK) p7_Fail(errmsg); p7_hmm_Destroy(top->hmm); esl_msa_Destroy(top->msa); esl_msa_Destroy(top->postmsa); tmp = top; top = tmp->next; tmp->next = empty; empty = tmp; ++next; } } else { /* queue up the msa so the sequence order is the same in * the .sto and .hmm */ if (empty != NULL) { tmp = empty; empty = tmp->next; } else { ESL_ALLOC(tmp, sizeof(PENDING_ITEM)); } tmp->nali = item->nali; tmp->hmm = item->hmm; tmp->msa = item->msa; tmp->postmsa = item->postmsa; tmp->entropy = item->entropy; /* add the msa to the pending list */ if (top == NULL || tmp->nali < top->nali) { tmp->next = top; top = tmp; } else { PENDING_ITEM *ptr = top; while (ptr->next != NULL && tmp->nali > ptr->next->nali) { ptr = ptr->next; } tmp->next = ptr->next; ptr->next = tmp; } } item->nali = 0; item->processed = FALSE; item->hmm = NULL; item->msa = NULL; item->postmsa = NULL; item->entropy = 0.0; } } } if (top != NULL) esl_fatal("Top is not empty\n"); while (empty != NULL) { tmp = empty; empty = tmp->next; free(tmp); } status = esl_workqueue_ReaderUpdate(queue, item, NULL); if (status != eslOK) esl_fatal("Work queue reader failed"); if (sstatus == eslEOF) { /* wait for all the threads to complete */ esl_threads_WaitForFinish(obj); esl_workqueue_Complete(queue); } return sstatus; ERROR: return eslEMEM; }
int main(void) { int i; int ncpu = 4; int iter = 25; WORK_INFO *worker = NULL; ESL_THREADS *thr = NULL; ESL_WORK_QUEUE *queue = NULL; int *objs = NULL; int *obj; objs = malloc(sizeof(int) * ncpu * 2); worker = malloc(sizeof(WORK_INFO) * ncpu); thr = esl_threads_Create(&worker_thread); /* Create a work queue that is able to hold two items per thread. * The idea is that while one object is being processed by a * worker thread, another item is being readied. So, when the * worker thread has completed processing its current object, * its next object to processes is hopefully waiting. */ queue = esl_workqueue_Create(ncpu * 2); for (i = 0; i < ncpu * 2; i++) { objs[i] = 0; esl_workqueue_Init(queue, &objs[i]); } for (i = 0; i < ncpu; i++) { worker[i].id = 'A' + i; worker[i].queue = queue; esl_threads_AddThread(thr, (void *) &worker[i]); } esl_threads_WaitForStart (thr); /* For N number of iterations, get an object that has been * processed, i.e. on the readers input queue and place it * on the ready queue. */ esl_workqueue_ReaderUpdate(queue, NULL, (void **) &obj); for (i = 1; i <= iter; ++i) { *obj = i; printf("Item %d is ready to be processed\n", *obj); esl_workqueue_ReaderUpdate(queue, obj, (void **) &obj); } /* put zeros on the queues to signal the worker that we are done */ for (i = 0; i < ncpu; ++i) { *obj = 0; esl_workqueue_ReaderUpdate(queue, obj, (void **) &obj); } /* The worker threads now run their work. */ esl_threads_WaitForFinish(thr); esl_threads_Destroy(thr); esl_workqueue_Destroy(queue); free(worker); free(objs); return eslOK; }