commit: b51db5ffb23808dcddf93bdbdfaf3a5ee0e391cc
Author: Kerin Millar <kfm <AT> plushkava <DOT> net>
AuthorDate: Tue Aug 13 02:08:59 2024 +0000
Commit: Sam James <sam <AT> gentoo <DOT> org>
CommitDate: Wed Aug 21 12:36:19 2024 +0000
URL:
https://gitweb.gentoo.org/proj/gentoo-functions.git/commit/?id=b51db5ff
Reduce the two non-bash srandom() implementations to just one
The implementation of srandom() that was written with mksh first and
foremost in mind is no longer as slow as it was. I decided to benchmark
30,000 iterations of both of the non-bash implementations with varying
maximal pool sizes. The results are beneath. Note that both "dash/1" and
"mksh/1" refer to the mksh-targeting implementation.
Pool Size dash/1 dash/2 mksh/1
48 B 6.67s 5.57s 58.84s
64 B 5.39s 4.78s 58.20s
96 B 5.49s 4.36s 58.13s
128 B 5.87s 4.63s 59.94s
160 B 5.93s 5.46s 64.64s
These figures demonstrate that the optimal pool size is roughly between
64 and 96 bytes, and that the performance of both implementations is now
comparable. In addition to testing Linux (6.6) on x86_64 hardware, I
experimented with the pool size on macOS Sonoma (using an Apple M1 CPU)
and found a value of 64 to be close to optimal.
In view of these findings, have _collect_entropy() collect 64 bytes at a
time and remove the marginally faster implementation. That is, the one
that depended on being able to perform arithmetic on a number as high as
2^32-1 without overflowing.
Additionally, increase the maximum number of times that the remaining
implementation tries to find a suitable sequence of hex digits from 2 to
3. Finally, remove the overflow check, for it is no longer required.
Signed-off-by: Kerin Millar <kfm <AT> plushkava.net>
functions.sh | 53 ++++++++++++-----------------------------------------
1 file changed, 12 insertions(+), 41 deletions(-)
diff --git a/functions.sh b/functions.sh
index 4598c5b..37ac8c2 100644
--- a/functions.sh
+++ b/functions.sh
@@ -580,12 +580,9 @@ srandom()
{
printf '%d\n' "$(( SRANDOM >> 1 ))"
}
- elif [ -c /dev/urandom ] && [ "$(( 1 << 31 == -2147483648 ))" -eq 1 ];
then
- # The shell implements integers as signed int rather than
- # signed long, contrary to the specification. Therefore, bit
- # shifting cannot be a viable strategy. Instead, try to discern
- # a suitably constrained sequence of 8 hex digits.
-
+ elif [ -c /dev/urandom ]; then
+ # Employ a method which entails searching for 8 consecutive hex
+ # digits, where the first is between 0 and 7.
genfun_int32_pat='[0-7][[:xdigit:]][[:xdigit:]][[:xdigit:]][[:xdigit:]][[:xdigit:]][[:xdigit:]][[:xdigit:]]'
unset -v genfun_entropy
@@ -594,7 +591,7 @@ srandom()
local hex i slice
# If the shell has forked, or if it cannot be determined
- # whether it has done so, repopulate the pool with 256
+ # whether it has done so, populate the pool with 64
# bytes worth of fresh entropy.
if ! _update_pid; then
_collect_entropy
@@ -603,7 +600,7 @@ srandom()
eval "genfun_pool_${genfun_pid}=1"
fi || return
- for i in 1 2; do
+ for i in 1 2 3; do
# shellcheck disable=2295
slice=${genfun_entropy%${genfun_int32_pat}*}
if [ "${#slice}" -ne "${#genfun_entropy}" ];
then
@@ -613,10 +610,9 @@ srandom()
hex=${hex%?}
done
break
- elif [ "$i" -eq 1 ]; then
- # The pool is too small to contain a
- # suitable sequence. Refill then try
- # again.
+ elif [ "$i" -lt 3 ]; then
+ # The pool does not contain a suitable
+ # sequence. Refill it then try again.
_collect_entropy
else
false
@@ -624,31 +620,6 @@ srandom()
done &&
printf '%d\n' "0x${hex}"
}
- elif [ -c /dev/urandom ]; then
- unset -v genfun_entropy
-
- srandom()
- {
- local hex
-
- if ! _update_pid; then
- # It cannot be determined whether the shell has
- # forked. Generate a number from 4 bytes worth
- # of fresh entropy.
- hex=$(LC_ALL=C od -vAn -N4 -tx1 /dev/urandom |
tr -d '[:space:]')
- test "${#hex}" -eq 8 && printf '%d\n' "$((
0x${hex} >> 1 ))"
- return
- elif [ "${#genfun_entropy}" -lt 8 ] || ! eval "test
\"\${genfun_pool_${genfun_pid}+set}\""; then
- # Either the pool is too small or the shell has
- # forked. Repopulate the pool with 256 bytes
- # worth of fresh entropy.
- _collect_entropy || return
- eval "genfun_pool_${genfun_pid}=1"
- fi
- hex=${genfun_entropy}
- genfun_entropy=${genfun_entropy%????????}
- printf '%d\n' "$(( 0x${hex#"$genfun_entropy"} >> 1 ))"
- }
else
warn "srandom: /dev/urandom doesn't exist as a character device"
return 1
@@ -799,12 +770,12 @@ whenceforth()
#------------------------------------------------------------------------------#
#
-# Collects 256 bytes worth of entropy from /dev/urandom and assigns it to the
-# genfun_entropy variable in the form of 512 hex digits.
+# Collects 64 bytes worth of entropy from /dev/urandom and assigns it to the
+# genfun_entropy variable in the form of 128 hex digits.
#
_collect_entropy() {
- genfun_entropy=$(LC_ALL=C od -vAn -N256 -tx1 /dev/urandom | tr -d
'[:space:]')
- test "${#genfun_entropy}" -eq 512
+ genfun_entropy=$(LC_ALL=C od -vAn -N64 -tx1 /dev/urandom | tr -d
'[:space:]')
+ test "${#genfun_entropy}" -eq 128
}
#