Subject: Optimization Opportunities 1 [long]
From: Sam TH (firstname.lastname@example.org)
Date: Mon Feb 05 2001 - 02:02:48 CST
Well, I've been playing with the wonderful gcov program (which has now
tripled the size of my source tree), and I have some interesting
data. This is just on startup time, one of the key measures that
users will judge the speed of AbiWord by. More on other aspects
First, basically all startup time is spent in the following functions:
linit (only with ispell)
EV_EditBindingMap::getShortcutFor(EV_EditMethod const *) const
1) linit - in src/other/spell/lookup.c
The gcov data tells us that there are only two sections of code, both
in for looks, that are executed a significant number of times. Each
of these loops ends up iterating about 30,000 times. For comparison,
I didn't see any other line in this function over 65 times. We
consider the second for loop first. [line 283]
31084 for (i = hashsize, dp = hashtbl; --i >= 0; dp++)
31083 if (dp->word == (char *) -1)
12 dp->word = NULL;
31071 dp->word = &hashstrings [ (int)(dp->word) ];
31083 if (dp->next == (struct dent *) -1)
19594 dp->next = NULL;
11489 dp->next = &hashtbl [ (int)(dp->next) ];
Basically, this loop is non-optimizeable. These are all just
assignments, and can't really be sped up. On to the first for loop.
31084 for( x=0; x<hashheader.tblsize; x++ )
31083 if( fread ( (char*)(hashtbl+x),
sizeof( struct dent)-sizeof( MASKTYPE ),
###### (void) fprintf (stderr, LOOKUP_C_BAD_FORMAT);
###### return (-1);
31083 } /*for*/
so, basically, this for loop consists of just one call, that to
fread. So, since we can't optimize fread, the problem boils down to
what we could use instead. Suggestions? Might read() be faster here?
Note also that this is not originally our code. The offending for
loop was actually written by Darren Benham, our illustrious Debian
maintainer, whom I've cc'ed on this mail. So we should investigate the
possibility that upstream has dealt with this issue .
 Does ispell upstream still exist?
Here's the definiton of this function:
inline const char * EV_EditMethod::getName(void) const
This is a rather large function, but again we have two for loops. For
loop 1 [line 352]
34873 for (j=0; j < EV_COUNT_EMS_NoShift; j++)
139330 if (m_pebChar->m_peb[i][j])
// only check non-null entries
32906 pEB = m_pebChar->m_peb[i][j];
32906 if ((pEB->getType() == EV_EBT_METHOD) &&
32906 (pEB->getMethod() == pEM))
81 bChar = UT_TRUE;
81 ems = EV_EMS_FromNumberNoShift(j);
For loop 2 [line 375]
112 for (i=0; (i < EV_COUNT_NVK) && !bNVK; i++)
6981 for (j=0; j < EV_COUNT_EMS; j++)
55797 if (m_pebNVK->m_peb[i][j])
// only check non-null entries
9350 pEB = m_pebNVK->m_peb[i][j];
9350 if ((pEB->getType() == EV_EBT_METHOD) &&
9350 (pEB->getMethod() == pEM))
9 bNVK = UT_TRUE;
9 ems = EV_EMS_FromNumber(j);
Whee, it's a nested for loop.
Ok, I have to admit that I don't have good suggestions for optimizing
these loops, since they mostly all involve assignments, macro calls
(EV_EMS_FromNumber) and calls to inlined accessor functions.
But the real question here is why we need to run these loops 60,000
times on start up. Ideas? Suggestions?
This is unix specific, but I wouldn't be surprised if the same
function took a while on other platforms.
This function, however, is likely to be very hard to optimize. It's
really long, consists of *lots* of GTK calls, and has no loops at
all. Significant investigation would be required to discover what
parts of this function, if any, take a long time. I may
instrumentalize it later this evening, if I get bored.
EV_EditBindingType EV_EditBinding::getType(void) const
Any objections to inlining this? That's all we can really do.
If anyone want more gcov data (I have it for every file in the tree)
just let me know.
Motto: start faster than vi (we're already faster than emacs).
This archive was generated by hypermail 2b25 : Mon Feb 05 2001 - 02:02:23 CST