Files
pv/docs/benchmark-pv-transfers.sh
T

569 lines
22 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/bin/bash
#
# Measure transfer rates and CPU usage with various different options,
# multiple times, then calculate the mean and standard deviation for each
# set of measurements.
#
# The report is written to stdout as tab-separated values, each line
# prefixed with an opaque system ID (based on "uname -a"), the PV version
# expressed as an integer, and a run ID based on the start date and time.
#
# Takes a path to a pv binary as an argument.
# Defaults.
pv='pv' # pv executable to run the measurements with
rounds='10' # how many rounds of measurements to take
testFileMB='256' # max size of each of the test files, in MiB
testZeroesMB='1024' # amount of /dev/zero data to use, in MiB
# Script information for --help and --version.
programName='benchmark-pv-transfers'
programVersion='0.1.0'
bugReportsTo='https://codeberg.org/ivarch/pv/issues'
copyrightYear='2026'
copyrightHolder='Andrew Wood'
# Constants.
fieldsPerRecord='4' # measurements taken: rate, time - real, user, sys.
# Define the measurements. Each measurement definition contains:
# - The ID of the measurement
# - The name of the measurement
# - A space-separated list of single-letter options that PV must support
# - How many testFileMB multiples are transferred, or Z for testZeroesMB
# - The command to run
# In the command to run, {PV} is replaced with the path to PV, {FILE1} and
# {FILE2} are replaced by the random data filenames, {ZSIZE} is replaced by
# testZeroesMB, and {OUTPUT1} and {OUTPUT2} are replaced by a temporary
# output filenames.
# Each definition is stored as single line, with the above parts separated
# by "!".
measurementDefinitions=''
# Add a definition (name, options list, data size, command) to the
# definitions array.
addDefinition () {
measurementDefinitions="$(
printf '%s\n%s!%s!%s!%s!%s\n' \
"${measurementDefinitions}" \
"$(printf '%s\n' "$1" | md5sum | cut -b1-7)" \
"$1" "$2" "$3" "$4" \
| grep .
)"
}
# Add a set of standard definitions for measurements, naming them with a
# prefix "$1: ", each with extra options "$2".
addStandardMeasurements () {
# From file via stdin to file via stdout.
addDefinition "$1: stdin file to file" "$2" 1 "{PV} $2 < {FILE1} > {OUTPUT1}"
# From file to file via stdout.
addDefinition "$1: file to file" "$2" 1 "{PV} $2 {FILE1} > {OUTPUT1}"
# From two files to file via stdout.
addDefinition "$1: two files to file" "$2" 2 "{PV} $2 {FILE1} {FILE2} > {OUTPUT1}"
# From pipe to file via stdout.
addDefinition "$1: pipe to file" "$2" 1 "cat {FILE1} | {PV} $2 > {OUTPUT1}"
# From file via stdin to pipe.
addDefinition "$1: stdin file to pipe" "$2" 1 "{PV} $2 < {FILE1} | cat > {OUTPUT1}"
# From file to pipe.
addDefinition "$1: file to pipe" "$2" 1 "{PV} $2 {FILE1} | cat > {OUTPUT1}"
# From two files to pipe.
addDefinition "$1: two files to pipe" "$2" 2 "{PV} $2 {FILE1} {FILE2} | cat > {OUTPUT1}"
# From pipe to pipe.
addDefinition "$1: pipe to pipe" "$2" 1 "cat {FILE1} | {PV} $2 | cat > {OUTPUT1}"
}
# Generate the measurement definitions, most of which are based on repeated
# blocks of the above standard measurements, with various different options.
defineMeasurements () {
addStandardMeasurements 'Default' ''
addStandardMeasurements 'No-splice' '-C'
addStandardMeasurements 'Pipe buffer 1M' '-J 1M'
addStandardMeasurements 'Transfer buffer 1M' '-B 1M'
addStandardMeasurements 'Directed output' '-o {OUTPUT2}'
addStandardMeasurements 'Directed output with no-splice' '-o {OUTPUT2} -C'
addStandardMeasurements 'Directed output with pipe buffer 1M' '-o {OUTPUT2} -J 1M'
addStandardMeasurements 'Directed output with transfer buffer 1M' '-o {OUTPUT2} -B 1M'
addStandardMeasurements 'Discard' '-X'
addStandardMeasurements 'Discard with no-splice' '-X -C'
addStandardMeasurements 'Discard with pipe buffer 1M' '-X -J 1M'
addStandardMeasurements 'Discard with transfer buffer 1M' '-X -B 1M'
addDefinition "Zeroes: stdout to /dev/null" '-S -s' Z "{PV} -S -s {ZSIZE}M /dev/zero > /dev/null"
addDefinition "Zeroes: stdout to pipe" '-S -s' Z "{PV} -S -s {ZSIZE}M /dev/zero | cat > /dev/null"
addDefinition "Zeroes: discarded" '-X -S -s' Z "{PV} -X -S -s {ZSIZE}M /dev/zero"
addDefinition "Zeroes: stdout to /dev/null with no-splice" '-C -S -s' Z "{PV} -C -S -s {ZSIZE}M /dev/zero > /dev/null"
addDefinition "Zeroes: stdout to pipe with no-splice" '-C -S -s' Z "{PV} -C -S -s {ZSIZE}M /dev/zero | cat > /dev/null"
addDefinition "Zeroes: discarded with no-splice" '-C -X -S -s' Z "{PV} -C -X -S -s {ZSIZE}M /dev/zero"
}
# Write an error message $1 to standard error, prefixed with the program
# name.
error () {
printf '%s: %s\n' "${programName}" "$1" >&2
}
# Output an error message $1, and exit with a failure status.
die () {
error "$1"
exit 1
}
# Show all measurement definitions.
showMeasurementDefinitions () {
printf '%s\n%s\n' 'ID!Name!Opts!Size!Command' "${measurementDefinitions}" \
| awk -F '!' '{printf "%s\t%-60s\t%s\n", $1, $2, $5}'
}
# Write a line of results for the measurement with ID $1 and name $2, round
# ${thisRound}, reading the times from ${workDir}/times and deriving the
# rate from the elapsed time (the real time in the times file) and the size
# written in ${workDir}/size. Removes those two files in the process.
#
# The results are also spooled to ${workDir}/results for later analysis,
# prefixed with only the ID $1.
resultsLine () {
testTimeReal="$(awk '$1=="real" {print $2}' "${workDir}/times")"
testTimeUser="$(awk '$1=="user" {print $2}' "${workDir}/times")"
testTimeSystem="$(awk '$1=="sys" {print $2}' "${workDir}/times")"
testRate="$(awk -v t="${testTimeReal}" '{if (t>0) { printf "%.3f\n", $1/t } else { print "-" }}' < "${workDir}/size")"
test -n "${testRate}" || testRate='-'
rm -f "${workDir}/times" "${workDir}/size"
printf '%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n' "${outputPrefix}" "${thisRound}" "$1" "${testRate}" "${testTimeReal}" "${testTimeUser}" "${testTimeSystem}" "$2"
printf '%s\t%s\t%s\t%s\t%s\n' "$1" "${testRate}" "${testTimeReal}" "${testTimeUser}" "${testTimeSystem}" >> "${workDir}/results"
}
# Run $2 in a shell under "time", writing $1 to ${workDir}/size and the
# times to ${workDir}/times.
captureTimes () {
printf '%s\n' "$1" > "${workDir}/size"
(
TIMEFORMAT="real %3R
user %3U
sys %3S"
time sh -c "{ $2; } 2>&3"
) 3>&2 2>"${workDir}/times"
rm -f "${workDir}/elapsed"
}
# Run all defined measurements for which the required options are available.
#
# If "${workDir}/permitted-measurements" is not empty, only measurements
# with IDs listed in that file will be taken.
gatherMeasurements () {
printf '%s\n' "${measurementDefinitions}" \
| {
while read -r definitionLine; do
measurementId="${definitionLine%%!*}"
measurementName="${definitionLine#*!}"
measurementName="${measurementName%%!*}"
requiredOptions="${definitionLine#*!}"
requiredOptions="${requiredOptions#*!}"
requiredOptions="${requiredOptions%%!*}"
dataSize="${definitionLine#*!}"
dataSize="${dataSize#*!}"
dataSize="${dataSize#*!}"
dataSize="${dataSize%%!*}"
templateCommand="${definitionLine##*!}"
test -n "${templateCommand}" || continue
if test -s "${workDir}/permitted-measurements"; then
grep -Fqx "${measurementId}" "${workDir}/permitted-measurements" || continue
fi
if test -n "${requiredOptions}"; then
optionLetters="$(printf 'x%s\n' "${requiredOptions}" | sed 's/-/\n-/g' | grep '^-' | cut -b 2)"
optionsPresent='true'
for pvOption in ${optionLetters}; do
grep -Fq " -${pvOption}" "${workDir}/help" || optionsPresent='false'
done
${optionsPresent} || continue
fi
activeCommand="$(
printf '%s\n' "${templateCommand}" | sed \
-e "s!{PV}!${pv}!g" \
-e "s!{FILE1}!${workDir}/file1!g" \
-e "s!{FILE2}!${workDir}/file2!g" \
-e "s!{ZSIZE}!${testZeroesMB}!g" \
-e "s!{OUTPUT1}!${workDir}/output1!g" \
-e "s!{OUTPUT2}!${workDir}/output2!g"
)"
if test "${dataSize}" = "Z"; then
dataSize="${testZeroesMB}"
else
dataSize=$((dataSize*testFileMB))
fi
captureTimes "${dataSize}" "${activeCommand}"
resultsLine "${measurementId}" "${measurementName}"
done
}
}
# Run several rounds of benchmark measurements using $1 as the pv
# executable, producing tab-separated data, including a final section
# containing the means and standard deviations for each measurement type.
#
# If $2 is not blank, only run the measurements whose IDs are listed in it.
#
runBenchmarks () {
pv="$1"
restrictTo="$2"
# Check there's enough room for the test files - make them smaller,
# if not.
tmpSpaceMB="$(df -kP "${TMPDIR:-/tmp}" | awk 'FNR==2 {print int($4/1024)}')"
while test "${testFileMB}" -gt 4; do
test "${tmpSpaceMB}" -gt $((2+3*testFileMB)) && break
testFileMB=$((testFileMB/2))
done
# Capture the help text so that capabilities can be checked.
${pv} -h > "${workDir}/help"
# Write a list of permitted measurement IDs, one per line. An empty
# file means no restriction.
printf '%s\n' "${restrictTo}" \
| tr ',; \t' '\n\n\n\n' \
| sort -u \
| grep . \
> "${workDir}/permitted-measurements"
# Define identifiers for the system this is running on, the pv
# version being benchmarked, and this specific benchmark run.
sysId="$(uname -a | md5sum | cut -b1-7)"
pvId="$(${pv} --version | awk 'FNR==1{print $2}' | awk -F . '{print 1000000*$1+1000*$2+$3}')"
runId="$(date '+%Y%m%d%H%M%S')"
outputPrefix="$(printf '%s\t%s\t%s\n' "${sysId}" "${pvId}" "${runId}")"
# Basic system information, and a header line for the test results.
printf '%s\t%s\t%s\n' "${outputPrefix}" 'System hostname' "$(uname -n)"
printf '%s\t%s\t%s\n' "${outputPrefix}" 'System load' "$(uptime | awk '{printf "%.2f\n",$(NF-2)}')"
printf '%s\t%s\t%s\n' "${outputPrefix}" 'System kernel type' "$(uname -s)"
printf '%s\t%s\t%s\n' "${outputPrefix}" 'System kernel release' "$(uname -r)"
printf '%s\t%s\t%s\n' "${outputPrefix}" 'System OS' "$(uname -o)"
printf '%s\t%s\t%s\n' "${outputPrefix}" 'PV path' "${pv}"
printf '%s\t%s\t%s\n' "${outputPrefix}" 'PV version' "$(${pv} -V | awk 'FNR==1 {print $2}')"
printf '%s\t%s\t%s\n' "${outputPrefix}" 'Test file size (MB)' "${testFileMB}"
# Generate two files of random data.
dd if='/dev/urandom' of="${workDir}/file1" bs=1048576 count="${testFileMB}" 2>/dev/null
dd if='/dev/urandom' of="${workDir}/file2" bs=1048576 count="${testFileMB}" 2>/dev/null
# Run several rounds of measurements.
printf '%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n' "${outputPrefix}" '#' 'ID' 'MiB/sec' 'Real time' 'User CPU time' 'System CPU time' 'Raw measurement'
thisRound=0
while test ${thisRound} -lt "${rounds}"; do
thisRound=$((1+thisRound))
gatherMeasurements
done
# For each of the types of measurement, report the mean and standard
# deviation of each field.
awk -F "\t" '{print $1}' < "${workDir}/results" > "${workDir}/measurement-ids"
true > "${workDir}/measurement-ids-used"
printf '%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n' "${outputPrefix}" 'μ/σ' 'ID' 'MiB/sec' 'Real time' 'User CPU time' 'System CPU time' 'Aggregated measurement'
{
while read -r measurementId; do
# Skip this type of measurement if already processed.
grep -Fqx "${measurementId}" "${workDir}/measurement-ids-used" && continue
printf '%s\n' "${measurementId}" >> "${workDir}/measurement-ids-used"
# Get the associated measurement name.
measurementName="$(printf '%s\n' "${measurementDefinitions}" | awk -F '!' -v "x=${measurementId}" '$1==x{print $2}')"
# Separate out this measurement type's results.
awk -F "\t" -v "mId=${measurementId}" '$1==mId {print}' < "${workDir}/results" \
> "${workDir}/measurements"
# Calculate the mean of each field.
awk -F "\t" -v "mId=${measurementId}" -v "mName=${measurementName}" -v "fieldcount=${fieldsPerRecord}" \
'BEGIN { samples=0 }
{ samples++; for (field=1; field<=fieldcount; field++) { total[field] += $(1+field) } }
END { printf "%s\t%s", "μ", mId; for (field=1; field<=fieldcount; field++) { printf "\t%.3f", total[field]/samples }; printf "\t%s\n", mName }' \
< "${workDir}/measurements" > "${workDir}/mean"
# Calculate the standard deviation of each field.
cat "${workDir}/mean" "${workDir}/measurements" \
| awk -F "\t" -v "mId=${measurementId}" -v "mName=${measurementName}" -v "fieldcount=${fieldsPerRecord}" \
'BEGIN { samples=0 }
FNR==1 { for (field=1; field<=fieldcount; field++) { mean[field] += $(2+field) } }
FNR>1 { samples++; for (field=1; field<=fieldcount; field++) { variance=$(1+field)-mean[field]; sum_variance_squared[field] += (variance*variance) } }
END { printf "%s\t%s", "σ", mId; for (field=1; field<=fieldcount; field++) { printf "\t%.3f", sqrt(sum_variance_squared[field]/samples) }; printf "\t%s\n", mName }' \
> "${workDir}/stddev"
sed "s!^!${outputPrefix}\t!" "${workDir}/mean" "${workDir}/stddev"
done
} < "${workDir}/measurement-ids"
}
# Reformat data on stdin so that each (whitespace-separated) column is
# space-padded and right-aligned to a consistent width for all rows.
#
# Note that this can only handle ASCII, not UTF-8.
lineUpColumns () {
tee "${workDir}/lineup-temp" \
| awk 'BEGIN {cols=0}
/./ {
if (NF > cols)
cols=NF
for (col=1; col<=NF; col++) {
if (length($col) >= max[col])
max[col]=length($col)
}
}
END {
for (col=1; col<=cols; col++) {
printf "%d ", max[col]
}
printf "\n"
}
' > "${workDir}/lineup-colwidths"
cat "${workDir}/lineup-colwidths" "${workDir}/lineup-temp" \
| awk '
FNR==1 { cols=NF; for (col=1; col<=NF; col++) { max[col]=$col } }
FNR>1 {
for (col=1; col<=cols; col++) {
if (col>1)
printf " "
val=(col<=NF ? $col : "")
printf "%" max[col] "s", val
}
printf "\n"
}'
}
# Read a stream of benchmark data on stdin containing runs from a single
# system, and report how the measurements changed across the different PV
# versions.
compareVersionResults () {
cat > "${workDir}/raw-system-data"
# List the measurement IDs in the order they appear in the data.
awk -F "\t" '$4=="σ"{print $5}' < "${workDir}/raw-system-data" > "${workDir}/measurement-ids"
# List all PV versions for which any data is available.
awk -F "\t" '$4=="PV version"{print $2,$5}' "${workDir}/raw-system-data" | sort -nu > "${workDir}/pv-versions"
# Report on each measurement type in turn.
true > "${workDir}/measurement-ids-seen"
{
while read -r measurementId; do
# Skip if this measurement was already processed.
grep -Fqx "${measurementId}" "${workDir}/measurement-ids-seen" && continue
printf '%s\n' "${measurementId}" >> "${workDir}/measurement-ids-seen"
# Collect this measurement's mean and standard deviation
# records for each PV version. If there's more than one for
# a single version, average them.
# TODO: split out collection into a separate function for re-use later
{
while read -r pvId pvVersion; do
awk -F "\t" \
-v "pvId=${pvId}" -v "outPrefix=${pvVersion}" \
-v "mId=${measurementId}" \
-v "fieldcount=${fieldsPerRecord}" \
'BEGIN {samples=0}
$2==pvId && $5==mId && $4=="μ" { samples++; for (field=1; field<=fieldcount; field++) { mean[field] += $(5+field) } }
$2==pvId && $5==mId && $4=="σ" { for (field=1; field<=fieldcount; field++) { stddev[field] += $(5+field) } }
END {
if (samples > 0) {
printf "%s", outPrefix
for (field=1; field<=fieldcount; field++) {
printf "\t%.3f\t%.3f", mean[field]/samples, stddev[field]/samples
}
printf "\n"
}
}' \
< "${workDir}/raw-system-data"
done
} < "${workDir}/pv-versions" > "${workDir}/measurements-per-version"
# If there are not at least 2 PV versions for which this
# measurement was available, report nothing as no comparison
# can be made.
test "$(grep -c . "${workDir}/measurements-per-version")" -lt 2 && continue
# Show the measurement name and associated command.
# Take the measurement name from the input data.
measurementName="$(awk -F "\t" -v "mId=${measurementId}" '$4=="σ" && $5==mId {print $NF;exit}' "${workDir}/raw-system-data")"
# Take the command from the definitions.
templateCommand="$(printf '%s\n' "${measurementDefinitions}" | awk -F '!' -v "mId=${measurementId}" '$1==mId {print $5}')"
printf '\n%s\n' "${measurementName}"
test -n "${templateCommand}" && printf ' (%s)\n' "${templateCommand}"
# Report each version's measurements and how they compare to
# the previous version.
#
# Since the column widths are set by an awk script which
# doesn't support UTF-8, ASCII headings are used initially,
# and adjusted after formatting.
{
printf '%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n' \
'PV' \
'M:Rate' 'S:Rate' 'C:Rate' \
'-' \
'M:tReal' 'S:tReal' 'C:tReal' \
'-' \
'M:tUser' 'S:tUser' 'C:tUser' \
'-' \
'M:tSys' 'S:tSys' 'C:tSys'
awk -F "\t" -v "fieldcount=${fieldsPerRecord}" \
'{
printf "%s", $1
for (field=0; field<fieldcount; field++) {
if (field > 0) {
printf "\t%s", "-";
}
mean=$(2+2*field)
stddev=$(3+2*field)
changescore=0
printf "\t%.3f\t%.3f", mean, stddev;
if (FNR>1) {
low=mean-stddev;
high=mean+stddev;
range=high-low;
prevlow=pmean[field]-pstddev[field];
prevhigh=pmean[field]+pstddev[field];
prevrange=prevhigh-prevlow;
if (prevrange < 1)
prevrange = 1;
if (high > prevhigh) {
changescore = (high - prevhigh) / prevrange;
} else if (low < prevlow) {
changescore = 0 - ((prevlow - low) / prevrange);
}
}
if (changescore < 0.01 && changescore > -0.01) {
printf "\t%d", 0
} else {
printf "\t%s%.2f", (changescore > 0.00 ? "+" : ""), changescore
}
pmean[field]=mean;
pstddev[field]=stddev;
}
printf "\n"
}' "${workDir}/measurements-per-version"
} \
| lineUpColumns \
| sed '1{s,M:,μ:,g;s,S:,σ:,g;s,C:,±:,g}'
done
} < "${workDir}/measurement-ids"
}
# Read a stream of benchmark data from stdin containing one or more runs
# from one or more systems and multiple PV versions, and for each individual
# system, report how the measurements changed across the different PV
# versions.
runVersionComparisons () {
cat > "${workDir}/raw-data"
# List the system IDs in the order they appear in the data.
awk -F "\t" '{print $1}' < "${workDir}/raw-data" | uniq > "${workDir}/sysids"
# Report on each system in turn.
true > "${workDir}/sysids-seen"
{
while read -r sysId; do
# Skip if this system was already processed.
grep -Fqx "${sysId}" "${workDir}/sysids-seen" && continue
printf '%s\n' "${sysId}" >> "${workDir}/sysids-seen"
# Extract the data for just this system.
awk -F "\t" -v "s=${sysId}" '$1==s {print}' < "${workDir}/raw-data" > "${workDir}/system-data"
# Report preamble.
printf '%79s\n' '' | tr ' ' '-'
printf '%s: %s - %s - %s %s\n' \
'System' \
"$(awk -F "\t" '$4=="System hostname"{print $5;exit}' "${workDir}/system-data")" \
"$(awk -F "\t" '$4=="System OS"{print $5;exit}' "${workDir}/system-data")" \
"$(awk -F "\t" '$4=="System kernel type"{print $5;exit}' "${workDir}/system-data")" \
"$(awk -F "\t" '$4=="System kernel release"{print $5;exit}' "${workDir}/system-data")"
cat <<EOF
For each measurement type, each of its aggregate benchmark results are
shown. Each field's mean and standard deviation are displayed along with a
change indicator showing how different this line's value is from the
previous line.
EOF
compareVersionResults < "${workDir}/system-data"
done
} < "${workDir}/sysids"
}
##############################################################################
# Main entry point.
# Process any command-line options.
action='benchmark'
restrictMeasurementIdList=''
while test -n "$1"; do
arg="$1"
shift
case "${arg}" in
'measurements'|'benchmark'|'analyse') action="${arg}" ;;
'-h'|'--help')
cat - <<EOF
Usage: ${programName} [OPTIONS] [ACTION]
Benchmark pv transfers.
Actions:
measurements - list all benchmark measurement definitions
benchmark - take several rounds of measurements
analyse - analyse benchmark data on stdin from multiple pv versions
Options:
-p, --program FILE benchmark using FILE as the pv executable
-r, --rounds ROUNDS run ROUNDS sets of measurements (${rounds})
-m, --measurement ID only run this specific measurement
-s, --size SIZE attempt to use a test file of SIZE MiB (${testFileMB})
-z, --zeroes SIZE stop at SIZE MiB for /dev/zero measurements (${testZeroesMB})
-h, --help show this help
-V, --version show script version
Default values are shown in brackets.
Please report any bugs to: ${bugReportsTo}
EOF
exit 0
;;
'-V'|'--version')
cat - <<EOF
${programName} (pv) ${programVersion}
Copyright ${copyrightYear} ${copyrightHolder}
License: GPLv3+ <https://www.gnu.org/licenses/gpl-3.0.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
EOF
exit 0
;;
'-p'|'--program'|'--pv') pv="$1"; test $# -gt 0 && shift ;;
'--program='*|'--pv='*) pv="${arg#*=}" ;;
'-r'|'--rounds') rounds="$1"; test $# -gt 0 && shift ;;
'--rounds='*) rounds="${arg#*=}" ;;
'-m'|'--measurement') restrictMeasurementIdList="${restrictMeasurementIdList} $1"; test $# -gt 0 && shift ;;
'--measurement='*) restrictMeasurementIdList="${restrictMeasurementIdList} ${arg#*=}" ;;
'-s'|'--size') testFileMB="$1"; test $# -gt 0 && shift ;;
'--size='*) testFileMB="${arg#*=}" ;;
'-z'|'--zeroes') testZeroesMB="$1"; test $# -gt 0 && shift ;;
'--zeroes='*) testZeroesMB="${arg#*=}" ;;
'-'*) die "${arg}: unknown option - try \`--help'" ;;
*) die "${arg}: unexpected argument - try \`--help'" ;;
esac
done
# Use /dev/shm for workspace if possible, to eliminate disk I/O as a factor.
if test -z "${TMPDIR}" && test -d '/dev/shm' && mountpoint -q '/dev/shm'; then
TMPDIR='/dev/shm'
export TMPDIR
fi
# Temporary working area, cleaned up on exit.
workDir="$(mktemp -d)" || exit 1
trap 'rm -rf "${workDir}"' EXIT
# Set everything to the "C" locale.
LANG=C
LC_ALL=C
export LANG LC_ALL
# Define the measurements.
defineMeasurements
# Run the selected action.
case "${action}" in
'measurements') showMeasurementDefinitions ;;
'benchmark') runBenchmarks "${pv}" "${restrictMeasurementIdList}" ;;
'analyse') runVersionComparisons ;;
esac