Zsh Mailing List Archive
Messages sorted by:
Reverse Date,
Date,
Thread,
Author
The speed of zsh
- X-seq: zsh-workers 2064
- From: Zoltan Hidvegi <hzoli@xxxxxxxxxx>
- To: zsh-workers@xxxxxxxxxxxxxxx (Zsh hacking and development)
- Subject: The speed of zsh
- Date: Sat, 24 Aug 1996 21:59:25 +0200 (MET DST)
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
Messages sorted by:
Reverse Date,
Date,
Thread,
Author