summaryrefslogtreecommitdiff
path: root/src/choices.c
diff options
context:
space:
mode:
authorJohn Hawthorn <john.hawthorn@gmail.com>2017-01-08 01:18:43 -0800
committerJohn Hawthorn <john.hawthorn@gmail.com>2017-01-08 02:02:22 -0800
commit5df8c7c5078a6c7faf927c2c5308c93c1f660e22 (patch)
treecb5b902ae04ecab21ad6ec7c64f577be2b01154b /src/choices.c
parent66d2c520910c57ae0d4d3afbb8fea74b862e3875 (diff)
Improve parallelism of search workers
Previously the list of candidates was split between threads a priori, with each thread being evenly distributed a contiguous range from the search candidates. This did a bad job of distributing the work evenly. There are likely to be areas with significantly more matches than others (ex. files within directories which match the search terms), as well as areas with longer strings than others (ex. deep directories). Because of the type of data fzy receives, work allocation needs to be dynamic. This commit changes the workers to operate on the candidates in batches, until they have all been processed. Batches are allocated by locking a mutex and grabbing the next available range of BATCH_SIZE candidates. BATCH_SIZE is currently set at 512, which worked best on my laptop in a quick test. This will always be a compromise. Small batch sizes will distribute the work more evenly, but larger batch sizes will be friendlier to CPU caches. Quick testing: Before: ./fzy -e drivers --benchmark < linux_files.txt 1.69s user 0.03s system 163% cpu 1.053 total After: ./fzy -e drivers --benchmark < linux_files.txt 2.12s user 0.02s system 296% cpu 0.721 total
Diffstat (limited to 'src/choices.c')
-rw-r--r--src/choices.c46
1 files changed, 39 insertions, 7 deletions
diff --git a/src/choices.c b/src/choices.c
index 619e339..2c789b6 100644
--- a/src/choices.c
+++ b/src/choices.c
@@ -138,9 +138,13 @@ size_t choices_available(choices_t *c) {
return c->available;
}
+#define BATCH_SIZE 512
+
struct search_job {
+ pthread_mutex_t lock;
choices_t *choices;
const char *search;
+ size_t processed;
};
struct worker {
@@ -151,19 +155,41 @@ struct worker {
size_t available;
};
+static void worker_get_next_batch(struct search_job *job, size_t *start, size_t *end) {
+ pthread_mutex_lock(&job->lock);
+
+ *start = job->processed;
+
+ job->processed += BATCH_SIZE;
+ if (job->processed > job->choices->size) {
+ job->processed = job->choices->size;
+ }
+
+ *end = job->processed;
+
+ pthread_mutex_unlock(&job->lock);
+}
+
static void *choices_search_worker(void *data) {
struct worker *w = (struct worker *)data;
struct search_job *job = w->job;
const choices_t *c = job->choices;
- size_t start = (w->worker_num) * c->size / c->worker_count;
- size_t end = (w->worker_num + 1) * c->size / c->worker_count;
+ size_t start, end;
+
+ for(;;) {
+ worker_get_next_batch(job, &start, &end);
+
+ if(start == end) {
+ break;
+ }
- for(size_t i = start; i < end; i++) {
- if (has_match(job->search, c->strings[i])) {
- w->results[w->available].str = c->strings[i];
- w->results[w->available].score = match(job->search, c->strings[i]);
- w->available++;
+ for(size_t i = start; i < end; i++) {
+ if (has_match(job->search, c->strings[i])) {
+ w->results[w->available].str = c->strings[i];
+ w->results[w->available].score = match(job->search, c->strings[i]);
+ w->available++;
+ }
}
}
@@ -176,6 +202,10 @@ void choices_search(choices_t *c, const char *search) {
struct search_job *job = calloc(1, sizeof(struct search_job));
job->search = search;
job->choices = c;
+ if (pthread_mutex_init(&job->lock, NULL) != 0) {
+ fprintf(stderr, "Error: pthread_mutex_init failed\n");
+ abort();
+ }
/* allocate storage for our results */
c->results = malloc(c->size * sizeof(struct scored_result));
@@ -208,6 +238,8 @@ void choices_search(choices_t *c, const char *search) {
free(w->results);
}
+
+ pthread_mutex_destroy(&job->lock);
free(workers);
if(*search) {