[net.emacs] Speed enhackment for Gosling Emacs

chris%umcp-cs.csnet@csnet-relay.arpa (02/28/84)

From:      Chris Torek <chris%umcp-cs.csnet@csnet-relay.arpa>

Here is a set of modifications for Emacs (#264), to speed up startup.
It makes a noticeable difference -- in fact, if I run an "emacs -q
-eexit-emacs" in "/", Emacs now finishes in less time than Vi!  (Of
course, most startups will be slower:  the "-q" option is our "quick
Emacs" mod; it bypasses the .emacs_pro file.)

All changes are contained within the file "macros.c".  Rather than
including a "diff" listing, I'll just describe what to do, and supply
some new routines.

*** Note
***
*** For #85, you would have to change the code that defines the
*** "nothing" procedure.  Instead of putting a 0 in directly,
*** you should introduce the "nothing" function earlier, and
*** then set
***	MacBodies[FindMac ("nothing")] = 0;
*** I think that will work.  Also, you can't use the DefMac
*** routine as given here.  The only part I changed was the code
*** that adds a new macro; the rest should probably remain the
*** same.
***
*** etoN

------------
Instructions
------------

.5 SAVE THE OLD macros.c FILE!!!

1. Add '#include <ctype.h>' after '#include "buffer.h"'

2. Delete the "static MacrosAreSorted;"

3. In FindMac
	3a. delete the unused "int cmp;"

	3b. Delete the line referencing MacrosAreSorted (macros will
	always be sorted)

4. Replace the old DefMac function with the one below

5. Delete the SortMacros function

6. Replace the old ScanMap function with the one below

7. In InitMacros
	7a. Between the last defproc() and the first ScanMap() call,
	add the lines

		NMacs = NewNames - MacBodies;
		QSortA (MacBodies, &MacBodies[NMacs - 1]);

	7b. Between the last ScanMap() and the for (i) loop, add the line

		QSortN (MacBodies, &MacBodies[NMacs - 1]);

	7c. Delete the call to SortMacros

----------------------------
New or replacement functions
----------------------------

/* define the named macro to have the given body (or mlisp proc)
   This procedure occupies a lot of time when loading mlisp packages
   and/or .emacs_pro's, so it's been tweaked. */
DefMac (s, body, IsMLisp)
union {
    struct keymap *b_keymap;
    char  *b_string;
} body;
char *s;
{
    register int    i;
    register struct BoundName  *p;
    if ((i = FindMac (s)) < 0) {
	register struct BoundName **bp;
        register char **np;
	register struct BoundName **be;
	if (NMacs >= maxmacs) {
	    error ("Too many macro definitions.");
	    return;
	}
	np = &MacNames[NMacs];
	bp = &MacBodies[NMacs++];
	be = &MacBodies[-i - 1];
	np[1] = 0;		/* sneaky! */
	while (bp > be) {
	    np[0] = np[-1];
	    bp[0] = bp[-1];
	    --np, --bp;
	}
	p = *bp = (struct BoundName  *) malloc (sizeof (struct BoundName));
	p -> b_name = *np = savestr (s);
	p -> b_StepThrough = 0;
    }
    else {
	if ((p = MacBodies[i]) -> b_binding == ProcBound) {
	    error ("%s is already bound to a wired procedure!", s);
	    return;
	}
	if (IsMLisp == -1	/* Its an autoload definition of an already
				   defined function, ignore it. */
		    && (p -> b_binding != MLispBound || p -> b_bound.b.b_start)
		    && p -> b_binding != AutoLoadBound)
	    return;
	if (p -> b_binding == KeyBound) {
	    register struct buffer *b;
	    for (b = buffers; b; b = b -> b_next)
		if (b -> b_mode.md_keys == p -> b_bound.b_keymap)
		    b -> b_mode.md_keys = 0;
	    if (CurrentGlobalMap == p -> b_bound.b_keymap)
		CurrentGlobalMap == &GlobalMap;
	    if (bf_mode.md_keys == p -> b_bound.b_keymap)
		bf_mode.md_keys == 0;
	    NextLocalKeymap = NextGlobalKeymap = 0;
	    free (p -> b_bound.b_keymap);
	}
	else if (p -> b_binding == AutoLoadBound
			|| p -> b_binding == MacroBound)
	    free (p -> b_bound.b_body);
    }
    if (IsMLisp == -1) {
	p -> b_binding = AutoLoadBound;
	p -> b_bound.b_body = savestr (body.b_string);
    }
    else if (IsMLisp == -2) {
	p -> b_binding = KeyBound;
	p -> b_bound.b_keymap = body.b_keymap;
    }
    else if (IsMLisp) {
	p -> b_binding = MLispBound;
	p -> b_bound.b.b_start = body.b_string;
    }
    else {
	p -> b_binding = MacroBound;
	p -> b_bound.b_body = savestr (body.b_string);
    }
}

static
ScanMap (map)
register struct keymap *map; {
    register struct BoundName *p;
    register c;
    for (c = 0; c < 0200; c++)
	if (p = map -> k_binding[c]) {
	    int     lo = 0,
	            hi = NMacs - 1;
	    register struct BoundName **m;
	    while (lo <= hi) {
		register mid = (lo + hi) >> 1;
		m = &MacBodies[mid];
		if (*m == p)
		    goto SkipIt;
		if (*m > p)
		    hi = mid - 1;
		else
		    lo = mid + 1;
	    }
	    m = &MacBodies[lo];
	    {
		register struct BoundName **j = NewNames++;
		while (j > m) {
		    j[0] = j[-1];
		    j--;
		}
	    }
	    *m = p;
	    NMacs++;
    SkipIt: 
	    if (islower (c))
		map -> k_binding[toupper (c)] = p;
	}
}

static
QSortN (l, h)
struct BoundName **l;
register struct BoundName **h;
{
    register struct BoundName **i,
			      **j;
    register char *s1,
		  *s2;
    if ((i = l) >= h)
	return;
    j = h;
partition: 
 /* while (strcmp ((*i) -> b_name, (*h) -> b_name) < 0) i++; */
    for (;;) {
	s1 = (*i) -> b_name;
	s2 = (*h) -> b_name;
	while (*s1 == *s2++)
	    if (*s1++ == 0)
		goto out1;
	if (*s1 > *--s2)
	    break;
	i++;
    }
out1:
 /* while (--j > i && strcmp ((*j) -> b_name, (*h) -> b_name) > 0); */
    while (--j > i) {
	s1 = (*j) -> b_name;
	s2 = (*h) -> b_name;
	while (*s1 == *s2++)
	    if (*s1++ == 0)
		goto out2;
	if (*s1 < *--s2)
	    break;
    }
out2:
    if (i < j) {
	register struct BoundName *t;
	t = *i, *i = *j, *j = t;
	goto partition;
    }
    if (i < h) {
	register struct BoundName *t;
	t = *i, *i = *h, *h = t;
	QSortN (i + 1, h);
    }
    QSortN (l, i - 1);
}

static
QSortA (l, h)
struct BoundName **l;
register struct BoundName **h;
{
    register struct BoundName **i,
			      **j;
    if ((i = l) >= h)
	return;
    j = h;
partition: 
    while (*i < *h)
	i++;
    while (--j > i && *j > *h);
    if (i < j) {
	register struct BoundName *t;
	t = *i, *i = *j, *j = t;
	goto partition;
    }
    if (i < h) {
	register struct BoundName *t;
	t = *i, *i = *h, *h = t;
	QSortA (i + 1, h);
    }
    QSortA (l, i - 1);
}

-----------------------------------------------------------

That's it!  The heart of the speedup is really in ScanMap, where
some 200 or so trips around the outer loop can use as an inner loop
a binary -- rather than linear -- search, which more often than not
actually locates something.

A further possible speedup could be gained by modifying
simplecoms.c and emacs.c such that self-insert is not bound to
anything until after the keymaps have been scanned.  But according
to the last profile I made, much more could be gained by optimizing
the read() and stat() system calls....
--
In-Real-Life: Chris Torek, Univ of MD Comp Sci
UUCP:	{seismo,allegra,brl-bmd}!umcp-cs!chris
CSNet:	chris@umcp-cs		ARPA:	chris.umcp-cs@CSNet-Relay