grep: replace the internal queue with a ring buffer

We know up front how many items we can have in the queue (-B/Bflag), so
pay the cost of those particular allocations early on.

The reduced queue maintenance overhead seemed to yield about an ~8%
improvement for my earlier `grep -C8 -r closefrom .` test.

MFC after:	2 weeks
This commit is contained in:
Kyle Evans 2020-12-09 05:27:45 +00:00
parent 281412ce7b
commit df546c3b73
3 changed files with 74 additions and 48 deletions

View File

@ -707,6 +707,8 @@ main(int argc, char *argv[])
if ((aargc == 0 || aargc == 1) && !Hflag) if ((aargc == 0 || aargc == 1) && !Hflag)
hflag = true; hflag = true;
initqueue();
if (aargc == 0 && dirbehave != DIR_RECURSE) if (aargc == 0 && dirbehave != DIR_RECURSE)
exit(!procfile("-")); exit(!procfile("-"));

View File

@ -149,6 +149,7 @@ char *grep_strdup(const char *str);
void grep_printline(struct str *line, int sep); void grep_printline(struct str *line, int sep);
/* queue.c */ /* queue.c */
void initqueue(void);
bool enqueue(struct str *x); bool enqueue(struct str *x);
void printqueue(void); void printqueue(void);
void clearqueue(void); void clearqueue(void);

View File

@ -6,6 +6,7 @@
* *
* Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav * Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav
* All rights reserved. * All rights reserved.
* Copyright (c) 2020 Kyle Evans <kevans@FreeBSD.org>
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions * modification, are permitted provided that the following conditions
@ -45,15 +46,33 @@ __FBSDID("$FreeBSD$");
#include "grep.h" #include "grep.h"
struct qentry { typedef struct str qentry_t;
STAILQ_ENTRY(qentry) list;
struct str data;
};
static STAILQ_HEAD(, qentry) queue = STAILQ_HEAD_INITIALIZER(queue); static long long filled;
static long long count; static qentry_t *qend, *qpool;
static struct qentry *dequeue(void); /*
* qnext is the next entry to populate. qlist is where the list actually
* starts, for the purposes of printing.
*/
static qentry_t *qlist, *qnext;
void
initqueue(void)
{
qlist = qnext = qpool = grep_calloc(Bflag, sizeof(*qpool));
qend = qpool + (Bflag - 1);
}
static qentry_t *
advqueue(qentry_t *itemp)
{
if (itemp == qend)
return (qpool);
return (itemp + 1);
}
/* /*
* Enqueue another line; return true if we've dequeued a line as a result * Enqueue another line; return true if we've dequeued a line as a result
@ -61,61 +80,65 @@ static struct qentry *dequeue(void);
bool bool
enqueue(struct str *x) enqueue(struct str *x)
{ {
struct qentry *item; qentry_t *item;
bool rotated;
item = grep_malloc(sizeof(struct qentry)); item = qnext;
item->data.dat = grep_malloc(sizeof(char) * x->len); qnext = advqueue(qnext);
item->data.len = x->len; rotated = false;
item->data.line_no = x->line_no;
item->data.boff = x->boff;
item->data.off = x->off;
memcpy(item->data.dat, x->dat, x->len);
item->data.file = x->file;
STAILQ_INSERT_TAIL(&queue, item, list); if (filled < Bflag) {
filled++;
if (++count > Bflag) { } else if (filled == Bflag) {
item = dequeue(); /* We had already filled up coming in; just rotate. */
free(item->data.dat); qlist = advqueue(qlist);
free(item); rotated = true;
return (true); free(item->dat);
} }
return (false); item->dat = grep_malloc(sizeof(char) * x->len);
} item->len = x->len;
item->line_no = x->line_no;
item->boff = x->boff;
item->off = x->off;
memcpy(item->dat, x->dat, x->len);
item->file = x->file;
static struct qentry * return (rotated);
dequeue(void)
{
struct qentry *item;
item = STAILQ_FIRST(&queue);
if (item == NULL)
return (NULL);
STAILQ_REMOVE_HEAD(&queue, list);
--count;
return (item);
} }
void void
printqueue(void) printqueue(void)
{ {
struct qentry *item; qentry_t *item;
while ((item = dequeue()) != NULL) { item = qlist;
grep_printline(&item->data, '-'); do {
free(item->data.dat); /* Buffer must have ended early. */
free(item); if (item->dat == NULL)
} break;
grep_printline(item, '-');
free(item->dat);
item->dat = NULL;
item = advqueue(item);
} while (item != qlist);
qlist = qnext = qpool;
filled = 0;
} }
void void
clearqueue(void) clearqueue(void)
{ {
struct qentry *item; qentry_t *item;
while ((item = dequeue()) != NULL) { item = qlist;
free(item->data.dat); do {
free(item); free(item->dat);
} item->dat = NULL;
item = advqueue(item);
} while (item != qlist);
qlist = qnext = qpool;
filled = 0;
} }