tl;dr use read -t 0.1 -N 0
for delay that small.
1 Why read?
If your script needs an interval to be little more precise, then use builtin read command instead of external sleep command, /bin/sleep or /usr/bin/sleep, which is the default on most system.
When the interval is tiny even like 0.1 seconds, invoking an external command can already show some noticeable runtime overhead, for example:
$ time for ((i=0; i<10; i++)); do /bin/sleep 0.1; done
0m1.024s
That is 2.4% overhead while using read has only 0.1% overhead as shown as follows:
$ time for ((i=0; i<10; i++)); do read -t 0.1 -N 0; done
0m1.001s
Option -N 0 ensures no key press would interrupt the delay except Ctrl+C.
2 Detailed tests
Using the code below to test the elapsed time:
time for ((i=0; i<I; i++)); do $CMD; done
The results are (it’s % overhead in the parentheses):
CMD | I=1000 | I=2000 | I=3000 |
---|---|---|---|
/bin/sleep 0.001 | 3.002s (200.2%) | 6.014s (200.7%) | 9.026s (200.9%) |
read -t 0.001 -N 0 | 1.113s (11.3%) | 2.226s (11.3%) | 3.339s (11.3%) |
builtin sleep 0.001 | 1.129s (12.9%) | 2.254s (12.7%) | 3.379s (12.6%) |
Note that a portion of overhead is contributed by the loop, for instance, I=10000, CMD=:, elapsed time is about 0.102 seconds. The third command is the builtin sleep for Bash, see builtin sleep.
3 Conclusions
From the results above, it’s pretty clear that the overhead of using external sleep is high and it seems to accumulate as I increases, which means it only gets less iterations within same duration over time.
read or builtin sleep has consistent overhead around 11% or 12%. I was surprised to see builtin sleep takes more time to execute than read.
If your script uses endless loop which utilizes tiny intervals and they are smaller than 0.1 seconds, then you should use read to delay the process instead of using sleep.
Moreover, a few years back, I noticed some strange memory use when endless loop with external commands. External command costs and that makes me port my PS1 into C extension for Bash.
It’s inevitable that you need external commands when writing a Bash script, but when there is a builtin command, use it even it looks bizarre by using read for delay.
4 Builtin sleep
Bash ships a builtin sleep, but it’s not enabled by default, it can be enabled via:
enable -f /usr/lib64/bash/sleep sleep
You might find it by locate on your system if it’s installed in different location.
Bahs’s version of sleep only supports seconds:
sleep: usage: sleep seconds[.fraction]
Less flexible than common sleep command, but it’s more than enough to use in scripting.
$ echo "foo" | ssh myremotemachine "read -t 2 -N 0; cat -"
ReplyDelete$ echo "foo" | ssh myremotemachine "sleep 2; cat -"
foo
That's a good point and not at the same time.
ReplyDeleteAs my post constantly uses the word "small," 2 seconds ain't small by any definitions. Also, there basically is a loop around.
If you are going to use small delay and also have user input, that doesn't make a lot of sense, right?
Still, I should have mentioned that there is a caveat for using read
It won't work in non-interactive scripts. You have to use it like this:
ReplyDeleteread -t 0.1 -N 0 </dev/zero
It needs some stdin for timeout to work.
It took me some time to figure out why '-t 10' has the same effect as '-t 0' :)
Thanks Tim for pointing out this important issue.
DeleteWhen I was writing this post, I believe it was prompted by Pipes (the pipes screensaver), which has delay like 0.025.
So, I wasn't even thinking about non-interactive case, but who knows who would use such minute in non-interactive script.
But if you, also @Tim, are having a delay of one second at least, just use `sleep`, you don't gain much from using `read`. Also, a small compatibility issue, IIRC, the read of Bash 3 (still default version on OS X, I believe) doesn't accept fraction.
I am using this in a bash script to smoothly change the mixer volume - I need a delay of 15 ms. External sleep is a huge overhead in this situation.
DeleteThat's a great example, why haven't I thought about that? My mind was stuck with something like cron tasks.
DeleteIs/will be it open source? Got a link?