raven-rhel6/rpm/rpm-4.8.x-macro-table-performance.patch
2024-02-21 20:14:44 +06:00

523 lines
14 KiB
Diff

@@ -, +, @@
Improve macro table performance
In the existing implementation, when a new macro is added, the whole
table has to be sorted again. Hence the cost of adding n macros is
worse than O(n^2), due to arithmetic progression.
This change drops all qsort(3) stuff altogether, by carefully preserving
table in sorted order. In findEntry routine, bsearch(3) is replaced
with customized binary search which tracks position for insertion.
In the addMacro routine, if a matching entry is not found, this
position is used for direct insertion, after the rest of the elements
are "shifted to the right" with memmove(3). Likewise, in delMacro
routine, the elements are shifted back to the left when the last macro
definition is popped. Technically, shifting half of the array with
memmove(3) is still O(n^2); however, modern CPUs process contiguous
memory in a very efficient manner, and glibc provides a fine-tuned
memmove(3) implementation.
Also, macro table entries are now allocated in a single chunk.
This change reduces rpm startup costs by factor of 6. Also, this change
improves specfile parser performance by a factor of 2 (e.g. the parse
time of texlive.spec is reduced from 67s to 35s).
Signed-off-by: Panu Matilainen <pmatilai@redhat.com>
--- rpm-4.8.0.orig/rpmio/macro.c 2009-12-07 09:36:49.000000000 -0500
+++ rpm-4.8.0.orig/rpmio/macro.c 2017-05-10 09:44:06.261704352 -0400
@@ -33,16 +33,15 @@ struct rpmMacroEntry_s {
struct rpmMacroEntry_s *prev;/*!< Macro entry stack. */
char *name; /*!< Macro name. */
char *opts; /*!< Macro parameters (a la getopt) */
- char *body; /*!< Macro body. */
int used; /*!< No. of expansions. */
int level; /*!< Scoping level. */
+ char body[]; /*!< Macro body. */
};
/*! The structure used to store the set of macros in a context. */
struct rpmMacroContext_s {
- rpmMacroEntry *macroTable; /*!< Macro entry table for context. */
- int macrosAllocated;/*!< No. of allocated macros. */
- int firstFree; /*!< No. of macros. */
+ rpmMacroEntry *tab; /*!< Macro entry table (array of pointers). */
+ int n; /*!< No. of macros. */
};
@@ -78,110 +77,31 @@ static int print_macro_trace = _PRINT_MA
#define _PRINT_EXPAND_TRACE 0
static int print_expand_trace = _PRINT_EXPAND_TRACE;
-#define MACRO_CHUNK_SIZE 16
-
/* forward ref */
static int expandMacro(MacroBuf mb);
/* =============================================================== */
-/**
- * Compare macro entries by name (qsort/bsearch).
- * @param ap 1st macro entry
- * @param bp 2nd macro entry
- * @return result of comparison
- */
-static int
-compareMacroName(const void * ap, const void * bp)
-{
- rpmMacroEntry ame = *((const rpmMacroEntry *)ap);
- rpmMacroEntry bme = *((const rpmMacroEntry *)bp);
-
- if (ame == NULL && bme == NULL)
- return 0;
- if (ame == NULL)
- return 1;
- if (bme == NULL)
- return -1;
- return strcmp(ame->name, bme->name);
-}
-
-/**
- * Enlarge macro table.
- * @param mc macro context
- */
-static void
-expandMacroTable(rpmMacroContext mc)
-{
- if (mc->macroTable == NULL) {
- mc->macrosAllocated = MACRO_CHUNK_SIZE;
- mc->macroTable = (rpmMacroEntry *)
- xmalloc(sizeof(*(mc->macroTable)) * mc->macrosAllocated);
- mc->firstFree = 0;
- } else {
- mc->macrosAllocated += MACRO_CHUNK_SIZE;
- mc->macroTable = (rpmMacroEntry *)
- xrealloc(mc->macroTable, sizeof(*(mc->macroTable)) *
- mc->macrosAllocated);
- }
- memset(&mc->macroTable[mc->firstFree], 0, MACRO_CHUNK_SIZE * sizeof(*(mc->macroTable)));
-}
-
-/**
- * Sort entries in macro table.
- * @param mc macro context
- */
-static void
-sortMacroTable(rpmMacroContext mc)
-{
- int i;
-
- if (mc == NULL || mc->macroTable == NULL)
- return;
-
- qsort(mc->macroTable, mc->firstFree, sizeof(*(mc->macroTable)),
- compareMacroName);
-
- /* Empty pointers are now at end of table. Reset first free index. */
- for (i = 0; i < mc->firstFree; i++) {
- if (mc->macroTable[i] != NULL)
- continue;
- mc->firstFree = i;
- break;
- }
-}
-
void
rpmDumpMacroTable(rpmMacroContext mc, FILE * fp)
{
- int nempty = 0;
- int nactive = 0;
-
if (mc == NULL) mc = rpmGlobalMacroContext;
if (fp == NULL) fp = stderr;
fprintf(fp, "========================\n");
- if (mc->macroTable != NULL) {
- int i;
- for (i = 0; i < mc->firstFree; i++) {
- rpmMacroEntry me;
- if ((me = mc->macroTable[i]) == NULL) {
- /* XXX this should never happen */
- nempty++;
- continue;
- }
- fprintf(fp, "%3d%c %s", me->level,
- (me->used > 0 ? '=' : ':'), me->name);
- if (me->opts && *me->opts)
- fprintf(fp, "(%s)", me->opts);
- if (me->body && *me->body)
- fprintf(fp, "\t%s", me->body);
- fprintf(fp, "\n");
- nactive++;
- }
+ for (int i = 0; i < mc->n; i++) {
+ rpmMacroEntry me = mc->tab[i];
+ assert(me);
+ fprintf(fp, "%3d%c %s", me->level,
+ (me->used > 0 ? '=' : ':'), me->name);
+ if (me->opts && *me->opts)
+ fprintf(fp, "(%s)", me->opts);
+ if (me->body && *me->body)
+ fprintf(fp, "\t%s", me->body);
+ fprintf(fp, "\n");
}
fprintf(fp, _("======================== active %d empty %d\n"),
- nactive, nempty);
+ mc->n, 0);
}
/**
@@ -189,34 +109,41 @@ rpmDumpMacroTable(rpmMacroContext mc, FI
* @param mc macro context
* @param name macro name
* @param namelen no. of bytes
+ * @param pos found/insert position
* @return address of slot in macro table with name (or NULL)
*/
static rpmMacroEntry *
-findEntry(rpmMacroContext mc, const char * name, size_t namelen)
+findEntry(rpmMacroContext mc, const char *name, size_t namelen, size_t *pos)
{
- rpmMacroEntry key, *ret;
- struct rpmMacroEntry_s keybuf;
- char *namebuf = NULL;
-
- if (mc == NULL) mc = rpmGlobalMacroContext;
- if (mc->macroTable == NULL || mc->firstFree == 0)
- return NULL;
-
- if (namelen > 0) {
- namebuf = xcalloc(namelen + 1, sizeof(*namebuf));
- strncpy(namebuf, name, namelen);
- namebuf[namelen] = '\0';
- name = namebuf;
- }
-
- key = &keybuf;
- memset(key, 0, sizeof(*key));
- key->name = (char *)name;
- ret = (rpmMacroEntry *) bsearch(&key, mc->macroTable, mc->firstFree,
- sizeof(*(mc->macroTable)), compareMacroName);
- _free(namebuf);
- /* XXX TODO: find 1st empty slot and return that */
- return ret;
+ /* bsearch */
+ int cmp = 1;
+ size_t l = 0;
+ size_t u = mc->n;
+ size_t i = 0;
+ while (l < u) {
+ i = (l + u) / 2;
+ rpmMacroEntry me = mc->tab[i];
+ if (namelen == 0)
+ cmp = strcmp(me->name, name);
+ else {
+ cmp = strncmp(me->name, name, namelen);
+ /* longer me->name compares greater */
+ if (cmp == 0)
+ cmp = (me->name[namelen] != '\0');
+ }
+ if (cmp < 0)
+ l = i + 1;
+ else if (cmp > 0)
+ u = i;
+ else
+ break;
+ }
+
+ if (pos)
+ *pos = (cmp < 0) ? i + 1 : i;
+ if (cmp == 0)
+ return &mc->tab[i];
+ return NULL;
}
/* =============================================================== */
@@ -676,53 +603,6 @@ exit:
}
/**
- * Push new macro definition onto macro entry stack.
- * @param mep address of macro entry slot
- * @param n macro name
- * @param o macro parameters (NULL if none)
- * @param b macro body (NULL becomes "")
- * @param level macro recursion level
- */
-static void
-pushMacro(rpmMacroEntry * mep,
- const char * n, const char * o,
- const char * b, int level)
-{
- rpmMacroEntry prev = (mep && *mep ? *mep : NULL);
- rpmMacroEntry me = (rpmMacroEntry) xmalloc(sizeof(*me));
-
- me->prev = prev;
- me->name = (prev ? prev->name : xstrdup(n));
- me->opts = (o ? xstrdup(o) : NULL);
- me->body = xstrdup(b ? b : "");
- me->used = 0;
- me->level = level;
- if (mep)
- *mep = me;
- else
- me = _free(me);
-}
-
-/**
- * Pop macro definition from macro entry stack.
- * @param mep address of macro entry slot
- */
-static void
-popMacro(rpmMacroEntry * mep)
-{
- rpmMacroEntry me = (*mep ? *mep : NULL);
-
- if (me) {
- /* XXX cast to workaround const */
- if ((*mep = me->prev) == NULL)
- me->name = _free(me->name);
- me->opts = _free(me->opts);
- me->body = _free(me->body);
- me = _free(me);
- }
-}
-
-/**
* Free parsed arguments for parameterized macro.
* @param mb macro expansion state
*/
@@ -730,21 +610,12 @@ static void
freeArgs(MacroBuf mb)
{
rpmMacroContext mc = mb->mc;
- int ndeleted = 0;
- int i;
-
- if (mc == NULL || mc->macroTable == NULL)
- return;
/* Delete dynamic macro definitions */
- for (i = 0; i < mc->firstFree; i++) {
- rpmMacroEntry *mep, me;
+ for (int i = 0; i < mc->n; i++) {
int skiptest = 0;
- mep = &mc->macroTable[i];
- me = *mep;
-
- if (me == NULL) /* XXX this should never happen */
- continue;
+ rpmMacroEntry me = mc->tab[i];
+ assert(me);
if (me->level < mb->depth)
continue;
if (strlen(me->name) == 1 && strchr("#*0", *me->name)) {
@@ -757,14 +628,11 @@ freeArgs(MacroBuf mb)
me->name, me->body, me->level);
#endif
}
- popMacro(mep);
- if (!(mep && *mep))
- ndeleted++;
+ /* compensate if the slot is to go away */
+ if (me->prev == NULL)
+ i--;
+ delMacro(mc, me->name);
}
-
- /* If any deleted macros, sort macro table */
- if (ndeleted)
- sortMacroTable(mc);
}
/**
@@ -1271,7 +1139,7 @@ expandMacro(MacroBuf mb)
}
/* Expand defined macros */
- mep = findEntry(mb->mc, f, fn);
+ mep = findEntry(mb->mc, f, fn, NULL);
me = (mep ? *mep : NULL);
/* XXX Special processing for flags */
@@ -1411,41 +1279,98 @@ void
addMacro(rpmMacroContext mc,
const char * n, const char * o, const char * b, int level)
{
- rpmMacroEntry * mep;
-
- if (mc == NULL) mc = rpmGlobalMacroContext;
-
- /* If new name, expand macro table */
- if ((mep = findEntry(mc, n, 0)) == NULL) {
- if (mc->firstFree == mc->macrosAllocated)
- expandMacroTable(mc);
- if (mc->macroTable != NULL)
- mep = mc->macroTable + mc->firstFree++;
- }
+ if (mc == NULL)
+ mc = rpmGlobalMacroContext;
- if (mep != NULL) {
- /* Push macro over previous definition */
- pushMacro(mep, n, o, b, level);
-
- /* If new name, sort macro table */
- if ((*mep)->prev == NULL)
- sortMacroTable(mc);
+ /* new entry */
+ rpmMacroEntry me;
+ /* pointer into me */
+ char *p;
+ /* calculate sizes */
+ size_t olen = o ? strlen(o) : 0;
+ size_t blen = b ? strlen(b) : 0;
+ size_t mesize = sizeof(*me) + blen + 1 + (olen ? olen + 1 : 0);
+
+ size_t pos;
+ rpmMacroEntry *mep = findEntry(mc, n, 0, &pos);
+ if (mep) {
+ /* entry with shared name */
+ me = xmalloc(mesize);
+ /* copy body */
+ p = me->body;
+ if (blen)
+ memcpy(p, b, blen + 1);
+ else
+ *p = '\0';
+ p += blen + 1;
+ /* set name */
+ me->name = (*mep)->name;
}
+ else {
+ /* extend macro table */
+ const int delta = 256;
+ if (mc->n % delta == 0)
+ mc->tab = xrealloc(mc->tab, sizeof(me) * (mc->n + delta));
+ /* shift pos+ entries to the right */
+ memmove(mc->tab + pos + 1, mc->tab + pos, sizeof(me) * (mc->n - pos));
+ mc->n++;
+ /* make slot */
+ mc->tab[pos] = NULL;
+ mep = &mc->tab[pos];
+ /* entry with new name */
+ size_t nlen = strlen(n);
+ me = xmalloc(mesize + nlen + 1);
+ /* copy body */
+ p = me->body;
+ if (blen)
+ memcpy(p, b, blen + 1);
+ else
+ *p = '\0';
+ p += blen + 1;
+ /* copy name */
+ me->name = memcpy(p, n, nlen + 1);
+ p += nlen + 1;
+ }
+
+ /* copy options */
+ if (olen)
+ me->opts = memcpy(p, o, olen + 1);
+ else
+ me->opts = o ? "" : NULL;
+ /* initialize */
+ me->used = 0;
+ me->level = level;
+ /* push over previous definition */
+ me->prev = *mep;
+ *mep = me;
}
void
delMacro(rpmMacroContext mc, const char * n)
{
- rpmMacroEntry * mep;
+ if (mc == NULL)
+ mc = rpmGlobalMacroContext;
- if (mc == NULL) mc = rpmGlobalMacroContext;
- /* If name exists, pop entry */
- if ((mep = findEntry(mc, n, 0)) != NULL) {
- popMacro(mep);
- /* If deleted name, sort macro table */
- if (!(mep && *mep))
- sortMacroTable(mc);
+ size_t pos;
+ rpmMacroEntry *mep = findEntry(mc, n, 0, &pos);
+ if (mep == NULL)
+ return;
+ /* parting entry */
+ rpmMacroEntry me = *mep;
+ assert(me);
+ /* detach/pop definition */
+ mc->tab[pos] = me->prev;
+ /* shrink macro table */
+ if (me->prev == NULL) {
+ mc->n--;
+ /* move pos+ elements to the left */
+ memmove(mc->tab + pos, mc->tab + pos + 1, sizeof(me) * (mc->n - pos));
+ /* deallocate */
+ if (mc->n == 0)
+ mc->tab = _free(mc->tab);
}
+ /* comes in a single chunk */
+ free(me);
}
int
@@ -1467,17 +1392,10 @@ rpmLoadMacros(rpmMacroContext mc, int le
if (mc == NULL || mc == rpmGlobalMacroContext)
return;
- if (mc->macroTable != NULL) {
- int i;
- for (i = 0; i < mc->firstFree; i++) {
- rpmMacroEntry *mep, me;
- mep = &mc->macroTable[i];
- me = *mep;
-
- if (me == NULL) /* XXX this should never happen */
- continue;
- addMacro(NULL, me->name, me->opts, me->body, (level - 1));
- }
+ for (int i = 0; i < mc->n; i++) {
+ rpmMacroEntry me = mc->tab[i];
+ assert(me);
+ addMacro(NULL, me->name, me->opts, me->body, (level - 1));
}
}
@@ -1553,25 +1471,13 @@ rpmInitMacros(rpmMacroContext mc, const
void
rpmFreeMacros(rpmMacroContext mc)
{
-
- if (mc == NULL) mc = rpmGlobalMacroContext;
-
- if (mc->macroTable != NULL) {
- int i;
- for (i = 0; i < mc->firstFree; i++) {
- rpmMacroEntry me;
- while ((me = mc->macroTable[i]) != NULL) {
- /* XXX cast to workaround const */
- if ((mc->macroTable[i] = me->prev) == NULL)
- me->name = _free(me->name);
- me->opts = _free(me->opts);
- me->body = _free(me->body);
- me = _free(me);
- }
- }
- mc->macroTable = _free(mc->macroTable);
+ if (mc == NULL)
+ mc = rpmGlobalMacroContext;
+ while (mc->n > 0) {
+ /* remove from the end to avoid memmove */
+ rpmMacroEntry me = mc->tab[mc->n - 1];
+ delMacro(mc, me->name);
}
- memset(mc, 0, sizeof(*mc));
}
char *