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 ----------