523 lines
14 KiB
Diff
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 *
|