2

In programming languages I would typically measure the performance of something relatively fast (<1 second completion) by looping it a few thousand times, and then getting the average time per completion.

How would I do the same in bash to compare the execution performance of two very fast commands?

2
  • use: $ time 'command' and $ man time for details on the time command itself.
    – user680858
    May 14, 2017 at 16:54
  • @WillemK I thought that only executes it once though to measure the speed? Or does it average a few executions?
    – Tiago
    May 14, 2017 at 16:56

5 Answers 5

5

It's better to use something like time command.

Bash also has a time built-in, what I'm suggesting is using time binary located at: /usr/bin/time.

For one runtime:

command time -f "\nElapsed: %E \nUser: %U \nSystem: %S \nMemory: %M\n" \
./MyCommand 1> /dev/null

Which outputs:

Elapsed: 0:00.01 
User: 0.00 
System: 0.00 
Memory: 2412
  • command: forces bash to use /usr/bin/time instead of time built-in.

You can use time with a loop to get a "avrage", "min", "max" of a specific resource say memory:

This code will run ./COMMAND commands 1000 times, then prints out the "min, max, avg" of its total (data+stack+text) memory usage.

#!/bin/bash
tmpfile=`mktemp`
for i in {1..1000}; do  command time -ao $tmpfile -f "%K" ./COMMAND 1>/dev/null; done;
awk 'NR == 1 {min = $0} $0 > max {max = $0} {total += $0} END {print total/NR, min, max}' $tmpfile
rm $tmpfile

Here is the output:

2436.89 2524 2324

You can change %K with:

  • %E: Elapsed real time
  • %I: Number of file inputs
  • %P: Percentage of the CPU that this job got
  • %k: Number of signals delivered to the process
  • %U: CPU usage in user mode
  • %S: CPU usage in kernel mode

See man time

Thanks to muru for writing a more clear awk statement.

5
  • That's an odd way to do it... Instead of the echo $(...) | tr ' ' '\n', you could just do for i in {1..1000}; do command time -f "%K\n" ./COMMAND 2>&1 1>/dev/null; done. And in awk, uninitialized variables get evaluated to 0, so awk '$0 < min {min = $0} $0 > max {max = $0} {total += $0} END {print total/NR; max, min}' The GNU time command also supports logging to a file, -o file, so you can use that, and then run awk on that file later, instead of redirecting stderr and stdout
    – muru
    May 14, 2017 at 17:47
  • oh, yes, min needs to be initialised to something other than 0. -v min=foo, or NR == 1 {min = $0}. Sorry about that
    – muru
    May 14, 2017 at 18:36
  • @muru fixed it ;) thanks again :-) gotta learn awk I guess :D
    – Ravexina
    May 14, 2017 at 18:43
  • 2
    A problem with this method is that /bin/time only uses centisecond resolution for CPU, so for very fast commands CPU will be zero. For example, the CPU used by running sleep .1, is rounded down to 0.00, so no matter how many iterations it runs, the CPU total will always be zero. (the built-in bash time has millisecond resolution). Also, the Elapsed time printed by %E is in the form [hours:]minutes:seconds.xxx (i.e. 0:00.11), so you can't total it with the simple awk + operator.
    – Johnny
    May 15, 2017 at 0:38
  • built-in time have not -f option, that's why you need to use command for /usr/bin/time. too bad git bash have not /usr/bin/time in windows. Jul 1, 2023 at 12:25
3

You can use Python's timeit module (which is usually used for python code snippets), with the subprocess.call function to run external commands:

$ python3 -m timeit -s 'import subprocess' 'subprocess.call(["sleep", "0.1"])' 
10 loops, best of 3: 103 msec per loop

You can change the number of loops with -n:

$ python3 -m timeit -n 5 -s 'import subprocess' 'subprocess.call(["sleep", "0.1"])' 
5 loops, best of 3: 103 msec per loop

And since sleep 0.1 should sleep for 100 ms, it looks like the measurement is pretty accurate.

Also see:

1

Nevermind, got it:

N=10000
time(for i in $(eval echo "{1..$N}"); do
./mycommand &>/dev/null
done)
3
  • 2
    for i in $(seq 1 $N) instead of the eval echo would be simpler. Or for ((i = 0; i < N; i++))
    – muru
    May 14, 2017 at 17:48
  • 1
    Don't use eval, it's both redundant here and recommended to be avoided in general. Use what muru suggested May 14, 2017 at 18:14
  • Please be aware, that sometimes the system will understand, that you want it to do the same thing many times, and find some shortcut, so that the second, third ... iterations will be faster than the first one.
    – sudodus
    Sep 13, 2020 at 7:13
0

In general the easy way is to do time ./myscript.sh but this will show you zero second as your script is really fast.

Instead use this answer that use GNU date (able to show nanoseconds) and PS4.

1
  • 1
    Please don't give links as answers. Instead, quote the relevant information from the link and give the link as a reference. Here, for example, you're linking to a question with multiple answers. Which one is the OP supposed to use? How would they adapt it for their specific problem?
    – terdon
    May 14, 2017 at 21:38
0

I was searching for something, and came across both this question and:

https://github.com/sharkdp/hyperfine - "A command-line benchmarking tool"

YMMV

0

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .