[comp.emacs] lisp environments

rpd@F.GP.CS.CMU.EDU (Richard Draves) (12/14/87)

>From: ralphw@IUS2.CS.CMU.EDU (Ralph Hyre)
>the only issue is where you take your performance hit.  Under X here,
>someone found that GNU emacs used ~22 system calls per keystroke.

Well, since that was me, let me post the full story.  I was working with
GNU Emacs 18.41 and X 10.4.  I have since hacked Emacs so that the basic
read echo loop takes 5 system calls (select/read/sigblock/write/sigsetmask)
and operations requiring more complicated redisplay (like ^V) will
do about as well (the number of write depends on how often Xlib's buffer
overflows).

29-Oct-87 22:59    Richard.Draves               more frightening GNU Emacs stats
From: Richard.Draves@F.GP.CS.CMU.EDU
I traced through all the processing GNU Emacs does for a normal
self-inserting character that doesn't need any special handling (eg,
makes buffer modified, triggers auto-filling, etc), when running under
X.  Without CMU modifications, to read and echo such a character takes
27 system calls.  (The system calls I count here are only those made
directly by GNU Emacs.)  With my (rather stupid) modifications (but
somehow they fit right in), it takes 36 system calls (5 more
sigsetmasks and 4 more sigblocks).

The 27 system calls breaks down as 7 FIONREAD ioctls, 5 sigsetmasks, 5
sigblocks, 5 writes, 3 kills, 1 select, and 1 read.  The writes are
particularly frightening, because potentially the display server wakes
up to process every one of them individually.  This is especially
costly if your Emacs is running on a remote machine.  I don't know
how a TCP/IP socket will actually handle the 5 writes.

I also looked at scrolling with C-v.  Fortunately, it doesn't do a
write for each line.  However, there is a large cost in sigblocks and
sigsetmasks, 5 each for lines that are longer than the old line and 7
each for lines that are shorter than the old line.  Considering
overhead system calls, this means 250 system calls is a very conservative
estimate of the average number required to scroll a 24-line page.  The
CMU modifications aren't significant here.

To convince you I'm not making this all up, here is a list of the
27 system calls used to read and echo a character.  I will start with
Emacs blocked in select, waiting for input.  It got there from
command_loop_1, which calls read_key_sequence, which calls get_char,
which calls kbd_buffer_get_char, which calls wait_reading_process_input,
which calls select.  Emacs has FASYNC set on the socket connecting it
to the display server, so when events come in it gets a SIGIO signal.
The signal handler reads and processes the X events.  Whenever code in
the main line wants to use any of the X code, it must protect against
a SIGIO signal with sigblock/sigsetmask.

1) select (in wait_reading_process_input)
[an X event comes in, unblocking the select & triggering the SIGIO handler]
2) ioctl (called by handler)
3) sigblock (called by XTread_socket)
4) ioctl (called by XPending)
5) read (called by XPending)
[the X event is processed & turned into a character]
6) ioctl (called by XPending)
7) sigblock (called by xfixscreen)
8) write (xfixscreen calls (an noop) XPixfill & XFlush)
9) sigsetmask (called by xfixscreen)
10) kill (called by xfixscreen)
[now a SIGIO is held]
11) sigsetmask (called by XTread_socket)
12) ioctl (called by handler)
[the handler exits but is immediately called again]
13) ioctl (called by handler)
[main line in wait_reading_process_input resumes & calls xfixscreen]
14) sigblock (called by xfixscreen)
15) write (called by xfixscreen)
16) sigsetmask (called by xfixscreen)
17) kill (called by xfixscreen)
[SIGIO handler is called now]
18) ioctl (called by handler)
[back to wait_reading_process_input, which returns back to command_loop_1,
which calls direct_output_for_insert, which calls XTwrite_chars, which
calls writechars]
19) sigblock (called by writechars)
20) write (CursorToggle (called by writechars) calls XPixSet/XFlush)
[writechars calls XText to echo the character]
21) write (CursorToggle (called by writechars) calls XPixSet/XFlush)
22) sigsetmask (called by writechars)
[writechars returns to command_loop_1, which calls read_key_sequence,
which calls get_char, which calls kbd_buffer_get_char, which calls
wait_reading_process_input, which calls xfixscreen]
23) sigblock (called by xfixscreen)
24) write (called by xfixscreen)
25) sigsetmask (called by xfixscreen)
26) kill (called by xfixscreen)
[SIGIO handler is called now]
27) ioctl (called by handler)
[now wait_reading_process_input goes to sleep in select, leaving us
back where we started]

Rich
----------