5

I can use time to get running time statistics:

% time zsh --version
zsh 5.6.2 (x86_64-pc-linux-gnu)
zsh --version  0.00s user 0.00s system 86% cpu 0.005 total

How can I time how long a source script takes to run?

% time source $NVM_DIR/nvm.sh
%
Tom Hale
  • 30,455

1 Answers1

1

It seems that zsh does not support to time builtin commands.

Try to use a shell that includes support for time as a reserved word (this applies to zsh) and also supports to time builtin commands (this does not apply to zsh).

BTW: this works for ksh88, ksh93, bosh (the current Bourne Shell), mksh and bash.

BTW: The correct way to do timing is to take the timing for the shell and all it's sub procecces (that may be called programs) and to print the difference from before and after the command. If the timing includes the time spent in the shell itself, this allows to time builtin commands.

The background is that the timing that works inside the shell should be compatible to what the external time command does and the external time command prints the sum of all times of all processes created for a specific command. If time did not include the values for sub processes, it would e.g. print too low values for the command cc as most of the work is done inside sub processes of cc.

schily
  • 19,173
  • 4
    It can only time processes. So time (source ...) or time (echo x) would work. Other shells may end up including the times of other processes when timing builtins or functions. See for instance bosh -c 'seq 10000000 > /dev/null & time read' that includes the user/sys time of seq if you press enter after seq has returned. IIRC mksh has some improvements on that front. – Stéphane Chazelas Oct 28 '18 at 08:59
  • This is a corner case. Not including sub processes when timing functions and builtins is also wrong. So you have to decide what is worse, being wrong in most cases or being wrong in some rare corner cases. – schily Oct 28 '18 at 09:11
  • zsh includes sub-processes when timing functions which you have to time with time (myfunction). zsh is right for all the times it reports. – Stéphane Chazelas Oct 28 '18 at 12:25
  • See how /usr/bin/time /bin/echo $(find / | wc -l) report something different from time /bin/echo $(find / | wc -l) in all shells that have a time keyword except zsh. – Stéphane Chazelas Oct 28 '18 at 12:27
  • What you describe as the correct way is wrong. A more correct way (which IIRC is the one implemented by mksh) would be to take the times before and after but substract the times of the child processes that are not part of the pipeline. In any case, the time of the child processes that have been started as part of the pipeline but haven't finished yet by the time the pipeline returns is not accounted for and cannot be. (typical in bash for things like time { cmd >(cmd2); } which generally fails to account cmd2's time (even time (cmd >(cmd2)) fails, zsh is OK). – Stéphane Chazelas Oct 28 '18 at 12:31
  • What you describe (keeping track of other process group times) is something that currently cannot be done in bosh as bosh does not implement a SIGCHLD handler and as bosh does not yet implement a list of recently finished job status data. bosh implements a similar method as csh and in all but a few corner cases reports better timing that other POSIX like shells do. With regards to your previous example: /usr/bin/time cannot see the times from the command substitution, but ksh88 already defines that the whole command is to be timed with the keyword time, so zsh is wrong. – schily Oct 28 '18 at 12:58
  • that zsh two shells have different APIs doesn't make any of them wrong. See how zsh reports both the times of cmd1 and cmd2 in cmd1 | cmd2. ksh doesn't do it, that doesn't mean it's wrong as it's not ksh's intention to reproduce the zsh behaviour. – Stéphane Chazelas Oct 28 '18 at 13:31
  • OK, but why should there be a builtin variant of time in the shell if you don't get any advantages from that fact? – schily Oct 28 '18 at 13:49
  • You do get an advantage. time foo | bar is much more useful in zsh than with the non-builtin time (or the time of other shells), it's also got it's TIMEFMT format à la csh, and you can time functions and builtins or compound commands (as long as you remember to put them in subshells, which I'll grant you, is somewhat of a limitation compared to ksh/bash but at least means it's reliable) which you can't do with an external time. – Stéphane Chazelas Oct 28 '18 at 16:39
  • The features with TIMEFORMAT in bosh are more useful than what you get with zsh in special as bosh supports up to microsecond granularity for the times and zsh usually just prints 0.00 for the timing of e.g. ls -l. I recommend you to try out the TIMEFORMAT sample value from the bosh man page. – schily Oct 28 '18 at 17:31
  • You can use %*U for instance on zsh to get millisecond precision, but getting that much precision (anything smaller than a scheduler typical time slice) doesn't make that much sense. If you want to time an operation with some level of precision, you'll be better off doing things like time (repeat 10000 that-operation) and take the average IMO. For elapsed time, you can also use typeset -F SECONDS like in ksh93 or use $EPOCHREALTIME to get nanoseconds, but again, that much precision doesn't make that much sense in shells that deal with commands having to be run in separate processes. – Stéphane Chazelas Oct 28 '18 at 18:11
  • Well I usually work on Solaris and the Solaris kernel implements micro state accounting (since approx. 15 years) that produces reliable microsecond values. So bosh gives you what you need for you need for performance mesurement. – schily Oct 28 '18 at 18:35
  • Yet as an illustration, testing bosh -c 'TIMEFORMAT=%6U; time ls' on a Solaris 11.4 VM here, I get 0.001383 on the first run, 0.000907 on the second, 0.001173 on the 3rd. It seems 1 ms is enough precision. /usr/bin/time bosh -c 'TIMEFORMAT=%6U; c=0; time while [ "$c" -lt 1000 ]; do ls; c=$(($c+1)); done' has bosh report 0.836859 user time while /usr/bin/time reports 0.0 user time. Might be something wrong with /usr/bin/time there. It also looks as if the zsh build on that machine doesn't account time of children processes. – Stéphane Chazelas Oct 28 '18 at 19:57
  • The precision inside the kernel is really nanoseconds. With ptime you get that resolution, but note there are variations from many sources e,g, interrupt scheduling and the time spent in the scheduler is not always the same. In multi user mode and with more CPUs, there is another imprecision... You get less variation if you write a CPU bound application and lock it to a specific CPU. You believe the results depend on the clock interrupt rate from the scheduler, so expect a variation of 10ms. I however only see a variation of approx. 100usec. It is better than what I get from Linux. – schily Oct 28 '18 at 22:56