1468
Comment:
|
3727
<() is very fast.
|
Deletions are marked like this. | Additions are marked like this. |
Line 7: | Line 7: |
The different approaches don't all produce identical results. Side effects are noted on the side for your information. |
|
Line 10: | Line 12: |
|| '''Time''' || '''Code''' || || 0m1.153s || {{{j=0; time for i in {1..100000}; do ((j++)); done}}} || || 0m1.381s || {{{j=0; time for i in {1..100000}; do j=$((j+1)); done}}} || || 0m1.498s || {{{j=0; time for i in {1..100000}; do let j++; done}}} || || 5m8.448s || {{{j=0; time for i in {1..100000}; do j=$(expr $j + 1); done}}} || As though you didn't already have enough reason to dump `expr`. |
|| 0m1.153s || {{{j=0; time for i in {1..100000}; do ((j++)); done}}} || An empty value is considered 0. || || 0m1.381s || {{{j=0; time for i in {1..100000}; do j=$((j+1)); done}}} || An empty value is considered 0. || || 0m1.498s || {{{j=0; time for i in {1..100000}; do let j++; done}}} || An empty value is considered 0. || || 0m1.726s || {{{j=0; time for i in {1..100000}; do j=$(($j+1)); done}}} || An empty value may cause bugs. In this case, leads to the valid `((+1))`. || || 5m8.448s || {{{j=0; time for i in {1..100000}; do j=$(expr "$j" + 1); done}}} || An empty value will cause errors. || |
Line 21: | Line 21: |
|| '''Time''' || '''Code''' || || 0m1.538s || {{{printf -v i '%s ' {1..100000}; time { echo "$i" >/dev/null; } }}} || || 0m3.112s || {{{printf -v i '%s ' {1..100000}; time { echo $i >/dev/null; } }}} || |
|| 0m1.510s || {{{printf -v i '%s ' {1..1000000}; time { : "$i"; } }}} |||| || 0m2.828s || {{{printf -v i '%s ' {1..1000000}; time { : $i; } }}} || Words in `i` undergo word splitting and pathname expansion. || |
Line 25: | Line 24: |
Another good reason to always quote your expansions. | == Expanding Multiple Arguments == || 0m2.828s || {{{printf -v i '%s ' {1..1000000}; time { : $i; } }}} || Arguments can only be words, they also undergo (likely unwanted) pathname expansion. || || 0m6.885s || {{{i=( {1..1000000} ); time { : "${i[@]}"; } }}} |||| |
Line 30: | Line 33: |
|| '''Time''' || '''Code''' || || 0m1.099s || {{{time for i in {1..100000}; do [[ -d . ]]; done}}} || || 0m2.110s || {{{time for i in {1..100000}; do [ -d . ]; done}}} || || 3m47.509s || {{{time for i in {1..100000}; do /bin/test -d .; done}}} || |
|| 0m1.099s || {{{time for i in {1..100000}; do [[ -d . ]]; done}}} || A keyword. Special (convenient) parsing rules apply. || || 0m2.110s || {{{time for i in {1..100000}; do [ -d . ]; done}}} || A built-in. || || 3m47.509s || {{{time for i in {1..100000}; do /bin/test -d .; done}}} || An external process. || |
Line 35: | Line 37: |
`[[` isn't just more featureful, it's also twice as fast. | == String Operations == || 0m0.149s || {{{path=/foo/bar; time for i in {1..10000}; do : "${path##*/}"; done}}} |||| || 0m19.284s || {{{path=/foo/bar; time for i in {1..10000}; do basename "$path" >/dev/null; done}}} || An external process. || || 0m21.289s || {{{path=/foo/bar; time for i in {1..10000}; do expr "$path" : '.*/\(.*\)' >/dev/null; done}}} || An external process. || || 0m24.789s || {{{path=/foo/bar; time for i in {1..10000}; do sed 's,.*/,,' <<< "$path" >/dev/null; done}}} || An external process. || == Iterate Parameters == || 0m0.239s || {{{set -- {1..100000}; time for x; do <(); done}}} || An empty process substitution is closer to a noop than `:`, which undergoes simple-command evaluation. `<()` and `>()` are Bash-only. || || 0m0.298s || {{{set -- {1..100000}; time for x; do :; done}}} |||| || 0m12.759s || {{{set -- {1..100000}; n=1; time while ((${!n+n++})); do :; done}}} |||| || 0m18.560s || {{{set -- {1..100000}; time while shift; do :; done}}} || Varies greatly between trials - repeated shifting of big argument lists causes cache misses. || || 0m38.831s || {{{set -- {1..100000}; n=1; time while ((n++<$#)); do :; done}}} || Calculating `$#` has some overhead. || == Invocation == {{{ $ TIMEFORMAT=%U,%S,%R; for sh in {{,m}k,z,{d,b}a}sh; do printf '%4s: %s\n' "$sh" "${ { time "$sh" -c ':' -- {1..100000}; } 2>&1; }"; done ksh: 0.060,0.000,0.070 mksh: 0.050,0.010,0.060 zsh: 0.060,0.000,0.070 dash: 0.060,0.010,0.060 bash: 27.660,0.010,27.710 }}} Bash isn't a good choice with e.g. `find ... --exec bash -c '...' -- {} +`. For some reason, setting arguments with `set --` is much faster in bash. ---- CategoryShell |
Some rough benchmarks
To compare different approaches to a problem with regards to performance, here's some rough benchmarks which may be system-dependent. Feel free to run each test yourself to see how your system fares.
Results are always sorted with the winners on top. All tests were repeated multiple times to make sure deviations did not exceed acceptable levels or cross result boundaries.
The different approaches don't all produce identical results. Side effects are noted on the side for your information.
Math
0m1.153s |
j=0; time for i in {1..100000}; do ((j++)); done |
An empty value is considered 0. |
0m1.381s |
j=0; time for i in {1..100000}; do j=$((j+1)); done |
An empty value is considered 0. |
0m1.498s |
j=0; time for i in {1..100000}; do let j++; done |
An empty value is considered 0. |
0m1.726s |
j=0; time for i in {1..100000}; do j=$(($j+1)); done |
An empty value may cause bugs. In this case, leads to the valid ((+1)). |
5m8.448s |
j=0; time for i in {1..100000}; do j=$(expr "$j" + 1); done |
An empty value will cause errors. |
Quoting
0m1.510s |
printf -v i '%s ' {1..1000000}; time { : "$i"; } |
|
0m2.828s |
printf -v i '%s ' {1..1000000}; time { : $i; } |
Words in i undergo word splitting and pathname expansion. |
Expanding Multiple Arguments
0m2.828s |
printf -v i '%s ' {1..1000000}; time { : $i; } |
Arguments can only be words, they also undergo (likely unwanted) pathname expansion. |
0m6.885s |
i=( {1..1000000} ); time { : "${i[@]}"; } |
Testing
0m1.099s |
time for i in {1..100000}; do [[ -d . ]]; done |
A keyword. Special (convenient) parsing rules apply. |
0m2.110s |
time for i in {1..100000}; do [ -d . ]; done |
A built-in. |
3m47.509s |
time for i in {1..100000}; do /bin/test -d .; done |
An external process. |
String Operations
0m0.149s |
path=/foo/bar; time for i in {1..10000}; do : "${path##*/}"; done |
|
0m19.284s |
path=/foo/bar; time for i in {1..10000}; do basename "$path" >/dev/null; done |
An external process. |
0m21.289s |
path=/foo/bar; time for i in {1..10000}; do expr "$path" : '.*/\(.*\)' >/dev/null; done |
An external process. |
0m24.789s |
path=/foo/bar; time for i in {1..10000}; do sed 's,.*/,,' <<< "$path" >/dev/null; done |
An external process. |
Iterate Parameters
0m0.239s |
set -- {1..100000}; time for x; do <(); done |
An empty process substitution is closer to a noop than :, which undergoes simple-command evaluation. <() and >() are Bash-only. |
0m0.298s |
set -- {1..100000}; time for x; do :; done |
|
0m12.759s |
set -- {1..100000}; n=1; time while ((${!n+n++})); do :; done |
|
0m18.560s |
set -- {1..100000}; time while shift; do :; done |
Varies greatly between trials - repeated shifting of big argument lists causes cache misses. |
0m38.831s |
set -- {1..100000}; n=1; time while ((n++<$#)); do :; done |
Calculating $# has some overhead. |
Invocation
$ TIMEFORMAT=%U,%S,%R; for sh in {{,m}k,z,{d,b}a}sh; do printf '%4s: %s\n' "$sh" "${ { time "$sh" -c ':' -- {1..100000}; } 2>&1; }"; done ksh: 0.060,0.000,0.070 mksh: 0.050,0.010,0.060 zsh: 0.060,0.000,0.070 dash: 0.060,0.010,0.060 bash: 27.660,0.010,27.710
Bash isn't a good choice with e.g. find ... --exec bash -c '...' -- {} +. For some reason, setting arguments with set -- is much faster in bash.