Zsh: measure command durations with the time command, TIMEFMT, and REPORTTIME

Artist’s depiction of the 'time' command

time is a POSIX utility for timing commands. Most shells build it in, and Zsh’s version has a lot more to offer than the one in Bash.

Put time before a command to time it. For example, to time a Python script:

$ time python example.py
python example.py  1.33s user 1.73s system 74% cpu 4.114 total

When the command finishes, Zsh prints a single line to standard error, which contains:

This one-line format differs from Bash’s time, which prints three lines, labelled real, user, and sys. But the numbers have the same meanings.

The CPU percentage is a useful clue for what a program is doing:

Pipelines and multiple commands

time applies to a single pipeline: one or more commands joined with |. Zsh reports each command in the pipeline separately:

$ time python example.py | cat
python example.py  1.32s user 1.26s system 70% cpu 3.665 total
cat  0.00s user 0.00s system 0% cpu 3.664 total

To time several commands together, wrap them in a subshell with parentheses:

$ time (python example.py; python example.py)
( python example.py; python example.py; )  3.34s user 3.03s system 74% cpu 8.539 total

Note that Zsh only times commands that run in a separate process. Unlike Bash, timing a brace group like { ...; } or a shell function prints nothing. Use a subshell for those too.

Customize the output with TIMEFMT

Zsh formats time’s output with the TIMEFMT parameter. The default value is:

TIMEFMT='%J  %U user %S system %P cpu %*E total'

That’s the job name, user time, system time, CPU percentage, and elapsed time, matching the output above. The * in %*E formats the time as hh:mm:ss.ttt, where hours and minutes only appear when non-zero, for example if we time a command taking >1 minute:

$ time sleep 61.5
sleep 61.5  0.00s user 0.00s system 0% cpu 1:01.50 total

Swap the * for m or u to get milliseconds or microseconds instead, which can be handy for timing fast commands. For example, to measure Python’s startup time in microseconds:

$ TIMEFMT='%J  %uU user %uS system %P cpu %uE total'
$ time python -c pass
python -c pass  19880us user 0us system 80% cpu 24773us total

Beyond times, TIMEFMT can report several resource usage statistics. The most useful is probably %M, the maximum memory the command used. For example:

$ TIMEFMT='%J  %M MB max memory'
$ time python example.py
python example.py  392 MB max memory

Unfortunately, %M’s unit depends on your OS. The Zsh documentation says kilobytes, which is what you get on macOS. But on Linux, a long-standing bug means you get megabytes instead (zsh-workers report)—woopsy. So, on macOS, label it KB rather than MB.

There are many more statistics available, like page faults, I/O operations, and context switches. See the TIMEFMT documentation for the full list.

Set TIMEFMT in your ~/.zshrc to use a custom format permanently.

Automatic reports with REPORTTIME

Zsh can also report statistics automatically, without needing time. Set REPORTTIME to a number of seconds, and any command that uses more CPU time than that will get a report:

$ REPORTTIME=1

$ python example.py
python example.py  1.46s user 1.58s system 73% cpu 4.128 total

Note that this threshold applies to CPU time, not elapsed time, so commands that mostly wait, like sleep 10, won’t trigger a report.

Fin

If you’re lost, you can look and you will find me, time after time,

—Adam


Read my book Boost Your Django DX, freshly updated in November 2024.


Subscribe via RSS, Twitter, Mastodon, or email:

One summary email a week, no spam, I pinky promise.

Related posts:

Tags: