[Rpm-maint] [PATCH 1/5] Optimize argvCount function

Alexey Tourbin alexey.tourbin at gmail.com
Sat Feb 9 05:00:13 UTC 2013


On Fri, Feb 08, 2013 at 04:24:28PM +0200, Panu Matilainen wrote:
> On 02/05/2013 06:31 AM, Alexey Tourbin wrote:
> >Callgrind annotations for 'rpmspec -P anaconda.spec', previous commit:
> >380,151,860  PROGRAM TOTALS
> >232,943,797  glibc-2.16-75f0d304/string/../sysdeps/x86_64/strlen.S:__GI_strlen
> >  63,770,642  rpmio/argv.c:argvCount
> >  13,702,038  build/parseSpec.c:readLine
> >...
> >
> >Callgrind annotations for 'rpmspec -P anaconda.spec', this commit:
> >364,226,458  PROGRAM TOTALS
> >232,943,797  glibc-2.16-75f0d304/string/../sysdeps/x86_64/strlen.S:__GI_strlen
> >  47,845,240  rpmio/argv.c:argvCount
> >  13,702,038  build/parseSpec.c:readLine
> >...
> >---
> >  rpmio/argv.c | 8 ++++----
> >  1 file changed, 4 insertions(+), 4 deletions(-)
> >
> >diff --git a/rpmio/argv.c b/rpmio/argv.c
> >index f061f03..d68422b 100644
> >--- a/rpmio/argv.c
> >+++ b/rpmio/argv.c
> >@@ -69,11 +69,11 @@ ARGint_t argiData(ARGI_const_t argi)
> >
> >  int argvCount(ARGV_const_t argv)
> >  {
> >-    int argc = 0;
> >+    ARGV_const_t argv_start = argv;
> >      if (argv)
> >-    while (argv[argc] != NULL)
> >-	argc++;
> >-    return argc;
> >+    while (*argv != NULL)
> >+	argv++;
> >+    return argv - argv_start;
> >  }
> >
> >  ARGV_t argvData(ARGV_t argv)
> >
> 
> No strong objections as such as this doesn't make it any more
> complicated than it was, but this falls into category of
> micro-optimizations which I'm not too fond of either:

It is not exactly a micro-optimization, since the argvCount(), as shown
for the case of 'rpmspec -P anaconda.spec', was the second most
expensive routine (and after argvJoin optimizations, is currently the
first most expensive routine).  Optimizing the most expensive routine by
30%, in my opinion, does not fall into the category of micro-optimization.

Of course, the real reason for this lies behind the fact that argvCount
gets called in a manner that amounts to O(n^2) complexity.  That is,
adding another argument with argvAdd() requires first to count
arguments with argvCount().  To add n-th argument, (n-1) previous
arguments are counted again, which forms an arithmetic progression.
(It can be helpful to visualize arithmetic progression as half of the
square below its diagonal.)

> The reason ARGV_t is used so widely is not because its a good data
> structure (for obvious reasons it is anything but) but because it
> has a convenient API that allowed replacing hand-coded variants of
> the same things all over the place. The idea always was to replace
> it with something saner where and when the need arises.
> Unfortunately its not an opaque data structure so it can't be just
> fixed behind the scenes, but still converting users of an API to
> another similar one is fairly close to an automated
> search-and-replace operation.

ARGV_t actually CAN be fixed.  The idea is that internal accounting
can be placed in memory BEFORE the user visible array.  Something like
this:

struct ARGV_header {
	size_t n;	/* vector fill */
	size_t m;	/* vector allocated slots */
	void *arena;	/* memory manager, to avoid strdup per arg */
	char *argv[];	/* user-visible vector */
}

size_t argvCount(ARGV_t argv)
{
	struct ARGV_header *h = (struct ARGV_header) argv - 1;
	return h->n;
}

The only requirement this implementation imposes is that you should not
try to create ARGV_t on your own - in other words, it does not accept
"foreign argv"s.  This requirement is typically satisfied with the

	ARGV_t argv = NULL;

declaration.

Perhaps I should indeed try to come up with a working implementation.
But that's another story.  At this point, I only try to make the
argvCount() loop as tight as possible, so that quadratic behaviour
does not strike so badly (i.e. so what I have to wait fewer seconds
when testing other changes over a large set of specfiles).

> You could think ARGV_t as a canary of sorts: if its dominating
> running-time, a better data structure is needed for that job. What
> the better fitting data structure is depends - for many things it'd
> be just a string array, but for example in this case it was used
> basically as a (very) poor hash-table replacement:
> http://lists.rpm.org/pipermail/rpm-maint/2011-January/002969.html
> 
> 	- Panu -


More information about the Rpm-maint mailing list