From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from euclid.skiles.gatech.edu (list@euclid.skiles.gatech.edu [130.207.146.50]) by coral.primenet.com.au (8.7.5/8.7.3) with ESMTP id GAA04194 for ; Sun, 25 Aug 1996 06:11:53 +1000 (EST) Received: (from list@localhost) by euclid.skiles.gatech.edu (8.7.3/8.7.3) id QAA04440; Sat, 24 Aug 1996 16:02:59 -0400 (EDT) Resent-Date: Sat, 24 Aug 1996 16:02:59 -0400 (EDT) From: Zoltan Hidvegi Message-Id: <199608241959.VAA01891@hzoli.ppp.cs.elte.hu> Subject: The speed of zsh To: zsh-workers@math.gatech.edu (Zsh hacking and development) Date: Sat, 24 Aug 1996 21:59:25 +0200 (MET DST) X-Mailer: ELM [version 2.4ME+ PL17 (25)] MIME-Version: 1.0 Content-Type: text/plain; charset=US-ASCII Content-Transfer-Encoding: 7bit Resent-Message-ID: <"J5xn8.0.F51.p_r7o"@euclid> Resent-From: zsh-workers@math.gatech.edu X-Mailing-List: archive/latest/2064 X-Loop: zsh-workers@math.gatech.edu Precedence: list Resent-Sender: zsh-workers-request@math.gatech.edu It's rumored that zsh is faster than bash. I made some experiments and I found that this is not really true. Executing the same script bash is about as fast as zsh and both ksh and pdksh are 2-3 times faster than zsh. This is true for executing a scipt containing only builtins as well as for scripts calling external commands. Somehow ksh spawns external commands twice as fast as zsh. I used Linux-2.0.10, ld.so.1.7.14, libc-5.3.12, AT&T ksh 12/28/93d, pdksh-5.2.7 and bash 1.14.6 for my tests. The patch below improves zsh preformance by 10-15%. An other 10% speed improvement would be possible by avoiding the child_block()/child_unblock() calls whenever possible (other shells do not use any system calls while executing builtin-only scritpts). Of course using special zsh features zsh scripts for the same task may be faster than sh/ksh scripts. I used a modified 99 bottle of beer script with beers=99999 bottles, zsh called as sh: s="s" while [ $beers -gt 0 ]; do : "$beers bottle$s of beer on the wall," : "$beers bottle$s of beer," : "take one down, pass it around," let beers=beers-1 if [ $beers -ne 0 ]; then test $beers -eq 1 && s="" : "$beers bottle$s of beer on the wall." else : "no bottles of beer on the wall." fi : done Execution time was 408.53s+31.71s, 99% CPU, 7:22.85 total Note that it is more than normal because of profiling, but the important part here is the 31.71s system time. ksh93 execution time was 97.13s+0.08s, 99% CPU, 1:38.08 total Flat profile for zsh with the patch below: Each sample counts as 0.01 seconds. % cumulative self self total time seconds seconds calls ms/call ms/call name 35.46 122.36 122.36 mcount 6.12 143.48 21.12 999998 0.02 0.16 execcmd 4.60 159.35 15.87 899991 0.02 0.04 paramsubst 3.96 173.02 13.67 20300232 0.00 0.00 halloc 2.70 182.34 9.32 3099986 0.00 0.01 stringsubst 2.32 190.34 8.00 999998 0.01 0.02 execpline 2.10 197.57 7.23 1200080 0.01 0.01 getvalue 1.88 204.07 6.50 4199985 0.00 0.00 untokenize 1.84 210.42 6.35 2699984 0.00 0.00 haswilds 1.63 216.03 5.61 899994 0.01 0.07 execbuiltin 1.62 221.61 5.58 1600000 0.00 0.04 prefork 1.58 227.07 5.46 15000174 0.00 0.00 hcalloc 1.58 232.52 5.45 4899955 0.00 0.00 dupstruct2 1.54 237.84 5.32 12299887 0.00 0.00 dupnode 1.41 242.70 4.86 400004 0.01 0.02 execlist 1.16 246.69 3.99 2300329 0.00 0.00 ztrdup 1.12 250.54 3.85 999998 0.00 0.00 execpline2 0.99 253.97 3.43 3099986 0.00 0.00 remnulargs 0.94 257.21 3.24 9999950 0.00 0.00 closemn 0.92 260.40 3.19 2900008 0.00 0.00 dupstring 0.89 263.48 3.08 2199984 0.00 0.00 zzlex 0.88 266.52 3.04 1800058 0.00 0.00 gethashnode 0.88 269.56 3.04 899994 0.00 0.01 mathevall 0.85 272.48 2.92 3599967 0.00 0.00 duplist 0.84 275.38 2.90 3000514 0.00 0.00 hasher 0.74 277.94 2.56 999998 0.00 0.00 deletejob 0.70 280.36 2.42 4799981 0.00 0.00 ugetnode 0.70 282.76 2.40 899994 0.00 0.01 mathparse 0.61 284.88 2.12 1199992 0.00 0.00 testlex 0.60 286.95 2.07 1099996 0.00 0.00 getstrvalue 0.57 288.93 1.98 3099986 0.00 0.00 filesubstr 0.57 290.91 1.98 299998 0.01 0.02 par_cond_2 0.57 292.87 1.96 999995 0.00 0.00 fixfds 0.57 294.82 1.95 899991 0.00 0.00 strcatsub 0.54 296.68 1.86 899994 0.00 0.00 zstrtol 0.52 298.49 1.81 1200153 0.00 0.00 gethashnode2 0.52 300.30 1.81 999998 0.00 0.00 initjob 0.50 302.02 1.72 299998 0.01 0.15 evalcond 0.50 303.73 1.71 3200034 0.00 0.00 zsfree 0.50 305.44 1.71 2699984 0.00 0.00 glob ... 0.00 345.09 0.00 1 0.00 0.00 zexit Replacing let beers=beers-1 with beers=`expr $beers - 1` in the script, using beers=9999 and unprofiled (so faster) zsh the execution time is 216.34s+239.19s, 99% CPU, 7:36.86 total ksh93 execution time is 92.37s+108.46s, 99% CPU, 3:21.31 total Note that ksh used less than half as much system time as zsh. Zoltan *** Src/exec.c 1996/08/15 10:39:14 2.84 --- Src/exec.c 1996/08/24 15:46:59 *************** *** 1642,1648 **** #ifdef HAVE_DEV_FD int i; ! for (i = 10; i < OPEN_MAX; i++) if (fdtable[i] > 1) fdtable[i]++; #endif --- 1642,1648 ---- #ifdef HAVE_DEV_FD int i; ! for (i = 10; i <= max_zsh_fd; i++) if (fdtable[i] > 1) fdtable[i]++; #endif *************** *** 1651,1657 **** subsh_close = -1; execshfunc(cmd, (Shfunc) hn); #ifdef HAVE_DEV_FD ! for (i = 10; i < OPEN_MAX; i++) if (fdtable[i] > 1) if (--(fdtable[i]) <= 2) zclose(i); --- 1651,1657 ---- subsh_close = -1; execshfunc(cmd, (Shfunc) hn); #ifdef HAVE_DEV_FD ! for (i = 10; i <= max_zsh_fd; i++) if (fdtable[i] > 1) if (--(fdtable[i]) <= 2) zclose(i); *************** *** 1917,1923 **** { int i; ! for (i = 10; i < OPEN_MAX; i++) if (fdtable[i] && (!how || fdtable[i] == how)) zclose(i); } --- 1917,1923 ---- { int i; ! for (i = 10; i <= max_zsh_fd; i++) if (fdtable[i] && (!how || fdtable[i] == how)) zclose(i); } *** Src/globals.h 1996/08/15 16:40:04 2.40 --- Src/globals.h 1996/08/15 16:40:04 *************** *** 394,399 **** --- 394,403 ---- EXTERN char fdtable[OPEN_MAX]; + /* The highest fd that marked with nonzero in fdtable */ + + EXTERN int max_zsh_fd; + /* input fd from the coprocess */ EXTERN int coprocin; *************** *** 555,560 **** --- 559,572 ---- #ifdef DEBUG EXTERN int alloc_stackp; #endif + + /* Variables used by signal queueing */ + + EXTERN int queueing_enabled; + EXTERN sigset_t signal_mask_queue[MAX_QUEUE_SIZE]; + EXTERN int signal_queue[MAX_QUEUE_SIZE]; + EXTERN int queue_front; + EXTERN int queue_rear; /* 1 if aliases should not be expanded */ *** Src/signals.c 1996/08/12 01:36:46 2.21 --- Src/signals.c 1996/08/23 21:14:25 *************** *** 343,397 **** return ret; } - - /* Use a circular queue to save signals caught during * - * critical sections of code. You call queue_signals to * - * start queueing, and unqueue_signals to process the * - * queue and stop queueing. Since the kernel doesn't * - * queue signals, it is probably overkill for zsh to do * - * this, but it shouldn't hurt anything to do it anyway. */ - - /* Right now I'm queueing all signals, but maybe we only * - * need to queue SIGCHLD. Anybody know? */ - - - #define MAX_QUEUE_SIZE 16 - - static int queueing_enabled; - static sigset_t signal_mask_queue[MAX_QUEUE_SIZE]; - static int signal_queue[MAX_QUEUE_SIZE]; - static int queue_front = 0; - static int queue_rear = 0; - - /* Start queueing up any received signals rather * - * than handling them. */ - - /**/ - void - queue_signals(void) - { - queueing_enabled++; /* signals are now being queued */ - } - - /* Process all queued signals */ - - /**/ - void - unqueue_signals(void) - { - DPUTS(!queueing_enabled, "BUG: unqueue_signals called but not queueing"); - if (--queueing_enabled) - return; - - while (queue_front != queue_rear) { /* while signals in queue */ - sigset_t oset; - queue_front = ++queue_front % MAX_QUEUE_SIZE; - oset = signal_setmask(signal_mask_queue[queue_front]); - handler(signal_queue[queue_front]); /* handle queued signal */ - signal_setmask(oset); - } - } - /* What flavor of waitpid/wait3/wait shall we use? */ #ifdef HAVE_WAITPID --- 343,348 ---- *** Src/signals.h 1996/06/28 02:05:24 2.2 --- Src/signals.h 1996/06/28 02:05:24 *************** *** 70,72 **** --- 70,98 ---- /* return a signal to it default action */ #define signal_default(S) signal(S, SIG_DFL) + /* Use a circular queue to save signals caught during * + * critical sections of code. You call queue_signals to * + * start queueing, and unqueue_signals to process the * + * queue and stop queueing. Since the kernel doesn't * + * queue signals, it is probably overkill for zsh to do * + * this, but it shouldn't hurt anything to do it anyway. */ + + /* Right now I'm queueing all signals, but maybe we only * + * need to queue SIGCHLD. Anybody know? */ + + #define MAX_QUEUE_SIZE 16 + + #define queue_signals() (queueing_enabled++) + + #define unqueue_signals() do { \ + DPUTS(!queueing_enabled, "BUG: unqueue_signals called but not queueing"); \ + if (!--queueing_enabled) { \ + while (queue_front != queue_rear) { /* while signals in queue */ \ + sigset_t oset; \ + queue_front = ++queue_front % MAX_QUEUE_SIZE; \ + oset = signal_setmask(signal_mask_queue[queue_front]); \ + handler(signal_queue[queue_front]); /* handle queued signal */ \ + signal_setmask(oset); \ + } \ + } \ + } while (0) *** Src/utils.c 1996/08/14 16:21:47 2.51 --- Src/utils.c 1996/08/23 22:13:59 *************** *** 868,879 **** #else int fe = movefd(dup(fd)); #endif ! close(fd); ! fdtable[fd] = 0; fd = fe; } ! if(fd != -1) fdtable[fd] = 1; return fd; } --- 868,881 ---- #else int fe = movefd(dup(fd)); #endif ! zclose(fd); fd = fe; } ! if(fd != -1) { fdtable[fd] = 1; + if (fd > max_zsh_fd) + max_zsh_fd = fd; + } return fd; } *************** *** 883,896 **** void redup(int x, int y) { ! if(x < 0) { ! close(y); ! fdtable[y] = 0; ! } else if (x != y) { dup2(x, y); ! fdtable[y] = fdtable[x]; ! close(x); ! fdtable[x] = 0; } } --- 885,897 ---- void redup(int x, int y) { ! if(x < 0) ! zclose(y); ! else if (x != y) { dup2(x, y); ! if ((fdtable[y] = fdtable[x]) && y > max_zsh_fd) ! max_zsh_fd = y; ! zclose(x); } } *************** *** 900,907 **** int zclose(int fd) { ! if (fd >= 0) fdtable[fd] = 0; return close(fd); } --- 901,911 ---- int zclose(int fd) { ! if (fd >= 0) { fdtable[fd] = 0; + while (!fdtable[max_zsh_fd]) + max_zsh_fd--; + } return close(fd); } EndOfPatch