speedtest-netperf: add idle latency measurement
authorTony Ambardar <itugrok@yahoo.com>
Tue, 30 Apr 2024 02:52:59 +0000 (19:52 -0700)
committerRosen Penev <rosenp@gmail.com>
Sun, 23 Jun 2024 23:10:00 +0000 (16:10 -0700)
Allow measuring ping latency and CPU details at idle as a baseline before
measuring under data transfer loading. This allows better determination of
Latency Under Load, a critical bufferbloat parameter. The CPU details can
also be used to verify idle conditions or examine CPU frequency against
ping variations and jitter.

Change the default test duration to 30 seconds, which is adequate for SQM
tuning while reducing bandwidth consumption for upstream netperf servers.

Change the default ping host from gstatic.com to one.one.one.one, which is
widely available and generally shows lower latency.

When warning of internal netperf errors, suggest running netperf directly
to view error details.

Other minor updates include:
  - clear tmp file names for safety in case of traps
  - simplify ping code, argument parsing and number validation
  - fix cases of wrong protocol usage with hostname as ping target
  - drop unneeded egrep usage

Also update README accordingly, with clearer usage text and terminology.

Signed-off-by: Tony Ambardar <itugrok@yahoo.com>
net/speedtest-netperf/Makefile
net/speedtest-netperf/files/README.md
net/speedtest-netperf/files/speedtest-netperf.sh

index cc3a5cc8a4921a2be3f2676d4440a2db68058e58..7f20a215cbd62fe7e2c152a86578d8529e72883c 100644 (file)
@@ -1,12 +1,12 @@
 #
-# Copyright (c) 2018 Tony Ambardar
+# Copyright (c) 2018-2024 Tony Ambardar
 # This is free software, licensed under the GNU General Public License v2.
 #
 
 include $(TOPDIR)/rules.mk
 
 PKG_NAME:=speedtest-netperf
-PKG_VERSION:=1.0.0
+PKG_VERSION:=1.1.0
 PKG_RELEASE:=1
 PKG_LICENSE:=GPL-2.0
 PKG_MAINTAINER:=Tony Ambardar <itugrok@yahoo.com>
index 7627748bcdb8051de3fd7ad9896baa33c4101d14..149ee0281dab2ebdb9f1a08bfd9e84f91fee8463 100644 (file)
@@ -11,7 +11,7 @@ The `speedtest-netperf` package provides a convenient means of on-device network
 
 3. **CPU Usage:**  Observing CPU usage under network load gives insight into whether the router is CPU-bound, or if there is CPU "headroom" to support even higher network throughput. In addition to managing network traffic, a router actively running a speed test will also use CPU cycles to generate network load, and measuring this distinct CPU usage also helps gauge its impact.
 
-**Note:** _The `speedtest-netperf.sh` script uses servers and network bandwidth that are provided by generous volunteers (not some wealthy "big company"). Feel free to use the script to test your SQM configuration or troubleshoot network and latency problems. Continuous or high rate use of this script may result in denied access. Happy testing!_
+**Note:** _The `speedtest-netperf.sh` script uses servers and network bandwidth that are provided by generous volunteers. Feel free to use the script to test your SQM configuration or troubleshoot network and latency problems, but be warned that continuous or high-rate use of the servers may result in denied access. Happy testing!_
 
 
 ## Theory of Operation
@@ -24,6 +24,8 @@ Sequential mode is preferred when measuring peak upload and download speeds for
 
 Concurrent mode places greater stress on the network, and can expose additional latency problems. It provides a more realistic estimate of expected bidirectional throughput. However, the download and upload speeds reported may be considerably lower than your line's rated speed. This is not a bug, nor is it a problem with your internet connection. It's because the ACK (acknowledge) messages sent back to the sender may consume a significant fraction of a link's capacity (as much as 50% with highly asymmetric links, e.g 15:1 or 20:1).
 
+The script also supports measuring latency when idle as a baseline prior to measuring under network load. This allows better determination of induced latency under load, a critical bufferbloat parameter. The CPU details can also be used to verify idle conditions or examine CPU frequency against ping variations and jitter (e.g. tracing high jitter to occasional low CPU frequency operation).
+
 After running `speedtest-netperf.sh`, if latency is seen to increase much during the data transfers, then other network activity, such as voice or video chat, gaming, and general interactive usage will likely suffer. Gamers will see this as frustrating lag when someone else uses the network, Skype and FaceTime users will see dropouts or freezes, and VOIP service may be unusable.
 
 ## Installation
@@ -42,9 +44,9 @@ opkg install speedtest-netperf_1.0.0-1_all.ipk
 
 ## Usage
 
-The speedtest-netperf.sh script measures throughput, latency and CPU usage during file transfers. To invoke it:
+The speedtest-netperf.sh script measures throughput, latency and CPU usage during file transfers and when idle. To invoke it:
 
-    speedtest-netperf.sh [-4 | -6] [-H netperf-server] [-t duration] [-p host-to-ping] [-n simultaneous-streams ] [-s | -c]
+    speedtest-netperf.sh [-4 | -6] [-H netperf-server] [-t duration] [-p host-to-ping] [-n simultaneous-streams ] [-s | -c [duration] ] [ -i [duration] ]
 
 Options, if present, are:
 
@@ -52,11 +54,12 @@ Options, if present, are:
     -H | --host:       DNS or Address of a netperf server (default - netperf.bufferbloat.net)
                        Alternate servers are netperf-east (US, east coast),
                        netperf-west (US, California), and netperf-eu (Denmark).
-    -t | --time:       Duration for how long each direction's test should run - (default - 60 seconds)
-    -p | --ping:       Host to ping to measure latency (default - gstatic.com)
+    -t | --time:       Duration for how long each direction's test should run - (default - 30 seconds)
+    -p | --ping:       Host to ping to measure latency (default - one.one.one.one)
     -n | --number:     Number of simultaneous sessions (default - 5 sessions)
-    -s | --sequential: Sequential download/upload (default - sequential)
-    -c | --concurrent: Concurrent download/upload
+    -s | --sequential: Sequential download/upload (default - disabled)
+    -c | --concurrent: Concurrent download/upload (default - disabled)
+    -i | --idle:       Measure idle latency before speed test (default - disabled)
 
 The primary script output shows download and upload speeds, together with the percent packet loss, and a summary of latencies, including min, max, average, median, and 10th and 90th percentiles so you can get a sense of the distribution.
 
@@ -69,7 +72,7 @@ Notice also that the activation of SQM requires greater CPU, but that in both ca
 
 ```
 [Sequential Test: NO SQM, POOR LATENCY]                       [Sequential Test: WITH SQM, GOOD LATENCY]
-# speedtest-netperf.sh                                        # speedtest-netperf.sh
+# speedtest-netperf.sh --sequential                           # speedtest-netperf.sh --sequential
 [date/time] Starting speedtest for 60 seconds per transfer    [date/time] Starting speedtest for 60 seconds per transfer
 session. Measure speed to netperf.bufferbloat.net (IPv4)      session. Measure speed to netperf.bufferbloat.net (IPv4)
 while pinging gstatic.com. Download and upload sessions are   while pinging gstatic.com. Download and upload sessions are
index f303f63129136b8447b453286885974b00efd80c..6e42589cfb6d068dbbfe920a9cec8c051bce786e 100644 (file)
 # first downloading and then uploading network streams, while concurrent mode
 # provides a stress test by dowloading and uploading streams simultaneously.
 #
+# The script also supports measuring latency when idle as a baseline prior to
+# testing under network load.
+#
 # NOTE: The script uses servers and network bandwidth that are provided by
-# generous volunteers (not some wealthy "big company"). Feel free to use the
-# script to test your SQM configuration or troubleshoot network and latency
-# problems.  Continuous or high rate use of this script may result in denied
-# access. Happy testing!
+# generous volunteers. Feel free to use the script to test your SQM
+# configuration or troubleshoot network and latency problems, but be warned
+# that continuous or high-rate use of the servers may result in denied access.
+# Happy testing!
 #
 # For more information, consult the online README.md:
 # https://github.com/openwrt/packages/blob/master/net/speedtest-netperf/files/README.md
 
-# Usage: speedtest-netperf.sh [-4 | -6] [ -H netperf-server ] [ -t duration ] [ -p host-to-ping ] [ -n simultaneous-streams ] [ -s | -c ]
+# Usage: speedtest-netperf.sh [-4 | -6] [ -H netperf-server ] [ -t duration ] [ -p host-to-ping ] [ -n simultaneous-streams ] [ -s | -c [duration] ] [ -i [duration] ]
 
 # Options: If options are present:
 #
 #                Alternate servers are netperf-east (east coast US),
 #                netperf-west (California), and netperf-eu (Denmark)
 # -4 | -6:       Enable ipv4 or ipv6 testing (ipv4 is the default)
-# -t | --time:   Duration of each direction's test - (default - 60 seconds)
-# -p | --ping:   Host to ping to measure latency (default - gstatic.com)
-# -n | --number: Number of simultaneous sessions (default - 5 sessions)
+# -t | --time:   Duration of each direction's test - (default - 30 seconds)
+# -p | --ping:   Host to ping to measure latency (default - one.one.one.one)
+# -n | --number: Number of simultaneous streams (default - 5 streams)
 #                based on whether concurrent or sequential upload/downloads)
-# -s | -c:       Sequential or concurrent download/upload (default - sequential)
+# -s | -c:       Sequential or concurrent download/upload (default - disabled)
+# -i | --idle:   Measure idle latency before speed test (default - disabled)
 
 # Copyright (c) 2014 - Rich Brown <rich.brown@blueberryhillsoftware.com>
-# Copyright (c) 2018 - Tony Ambardar <itugrok@yahoo.com>
+# Copyright (c) 2018-2024 - Tony Ambardar <itugrok@yahoo.com>
 # GPLv2
 
 
@@ -163,7 +167,7 @@ sample_load() {
        cat /proc/$$/stat
        while : ; do
                sleep 1s
-               egrep "^cpu[0-9]*" /proc/stat
+               grep "^cpu[0-9]*" /proc/stat
                for c in $cpus; do
                        [ -r $c/$f ] && echo "cpufreq $(basename $c) $(cat $c/$f)"
                done
@@ -179,13 +183,13 @@ print_dots() {
        done
 }
 
-# Start $MAXSESSIONS datastreams between netperf client and server
+# Start $MAXSTREAMS datastreams between netperf client and server
 # netperf writes the sole output value (in Mbps) to stdout when completed
 
 start_netperf() {
-       for i in $( seq $MAXSESSIONS ); do
-               netperf $TESTPROTO -H $TESTHOST -t $1 -l $TESTDUR -v 0 -P 0 >> $2 &
-#              echo "Starting PID $! params: $TESTPROTO -H $TESTHOST -t $1 -l $TESTDUR -v 0 -P 0 >> $2"
+       for i in $( seq $MAXSTREAMS ); do
+               netperf $TESTPROTO -H $TESTHOST -t $1 -l $SPEEDDUR -v 0 -P 0 >> $2 &
+#              echo "Starting PID $! params: $TESTPROTO -H $TESTHOST -t $1 -l $SPEEDDUR -v 0 -P 0 >> $2"
        done
 }
 
@@ -258,6 +262,50 @@ kill_background_and_exit() {
        exit 1
 }
 
+# Measure ping latency at idle as a baseline for comparison.
+
+measure_idle() {
+
+       # Create temp files for netperf up/download results
+       PINGFILE=$(mktemp /tmp/measurepings.XXXXXX) || exit 1
+       LOADFILE=$(mktemp /tmp/measureload.XXXXXX) || exit 1
+#      echo $PINGFILE $LOADFILE
+
+       # Start dots
+       print_dots &
+       DOTS_PID=$!
+#      echo "Dots PID: $DOTS_PID"
+
+       # Start Ping
+       ping $TESTPROTO $PINGHOST > $PINGFILE &
+       PING_PID=$!
+#      echo "Ping PID: $PING_PID"
+
+       # Start CPU load sampling
+       sample_load > $LOADFILE &
+       LOAD_PID=$!
+#      echo "Load PID: $LOAD_PID"
+
+       # Wait for idle test period
+       sleep $IDLEDUR
+
+       # When idle time elapses, stop the CPU monitor, dots and pings
+       kill_load
+       kill_pings
+       kill_dots
+       echo
+
+       # Summarize the ping data
+       summarize_pings $PINGFILE
+
+       # Summarize the load data
+       summarize_load $LOADFILE
+
+       # Clean up
+       rm -f $PINGFILE
+       rm -f $LOADFILE
+}
+
 # Measure speed, ping latency and cpu usage of netperf data transfers
 # Called with direction parameter: "Download", "Upload", or "Bidirectional"
 # The function gets other info from globals and command-line arguments.
@@ -280,11 +328,7 @@ measure_direction() {
 #      echo "Dots PID: $DOTS_PID"
 
        # Start Ping
-       if [ $TESTPROTO -eq "-4" ]; then
-               ping  $PINGHOST > $PINGFILE &
-       else
-               ping6 $PINGHOST > $PINGFILE &
-       fi
+       ping $TESTPROTO $PINGHOST > $PINGFILE &
        PING_PID=$!
 #      echo "Ping PID: $PING_PID"
 
@@ -308,7 +352,10 @@ measure_direction() {
 
        # Wait until background netperf processes complete, check errors
        if ! wait_netperf; then
-               echo;echo "WARNING: netperf returned errors. Results may be inaccurate!"
+               echo
+               echo "WARNING: Results may be inaccurate since 'netperf' returned errors."
+               echo "         Run directly for more details:"
+               echo "         netperf $TESTPROTO -H $TESTHOST"
        fi
 
        # When netperf completes, stop the CPU monitor, dots and pings
@@ -338,71 +385,115 @@ measure_direction() {
        rm -f $LOADFILE
 }
 
+print_usage() {
+       echo \
+"Usage: speedtest-netperf.sh [ -H netperf-server ] [ -p host-to-ping ] [-4 | -6]
+                            [ -i [duration] ] [ -s | -c [duration] ]
+                            [ -t duration ] [ -n simultaneous-streams ]"
+}
+
+is_number() {
+       case "$1" in
+               ""|*[![:digit:]]*) return 1;;
+               *) return 0 ;;
+       esac
+}
+
 # ------- Start of the main routine --------
 
-# set an initial values for defaults
+# Set initial values for defaults
 TESTHOST="netperf.bufferbloat.net"
-TESTDUR="60"
-PINGHOST="gstatic.com"
-MAXSESSIONS=5
+PINGHOST="one.one.one.one"
+MAXSTREAMS=5
 TESTPROTO="-4"
-TESTSEQ=1
-
-# read the options
-
-# extract options and their arguments into variables.
+TESTSPEED=0
+SPEEDDUR="30"
+TESTIDLE=0
+IDLEDUR="30"
+
+# Clear temp files
+DLFILE=
+ULFILE=
+PINGFILE=
+LOADFILE=
+
+# Parse options and their parameters into variables. Options for --idle,
+# --sequential and --concurrent have optional parameters.
 while [ $# -gt 0 ]
 do
        case "$1" in
-               -s|--sequential) TESTSEQ=1 ; shift 1 ;;
-               -c|--concurrent) TESTSEQ=0 ; shift 1 ;;
+               -i|--idle)
+                       TESTIDLE=1 ; shift 1
+                       is_number "$1" && { IDLEDUR=$1 ; shift 1 ; } ;;
+               -s|--sequential)
+                       TESTSPEED=1 ; shift 1
+                       is_number "$1" && { SPEEDDUR=$1 ; shift 1 ; } ;;
+               -c|--concurrent)
+                       TESTSPEED=2 ; shift 1
+                       is_number "$1" && { SPEEDDUR=$1 ; shift 1 ; } ;;
                -4|-6) TESTPROTO=$1 ; shift 1 ;;
                -H|--host)
                        case "$2" in
                                "") echo "Missing hostname" ; exit 1 ;;
-                               *) TESTHOST=$2 ; shift 2 ;;
+                               *) TESTHOST="$2" ; shift 2 ;;
                        esac ;;
                -t|--time)
-                       case "$2" in
-                               "") echo "Missing duration" ; exit 1 ;;
-                               *) TESTDUR=$2 ; shift 2 ;;
-                       esac ;;
+                       is_number "$2" || { echo "Missing duration" ; exit 1 ; }
+                       IDLEDUR=$2 ; SPEEDDUR=$2 ; shift 2 ;;
                -p|--ping)
                        case "$2" in
                                "") echo "Missing ping host" ; exit 1 ;;
                                *) PINGHOST=$2 ; shift 2 ;;
                        esac ;;
                -n|--number)
-                       case "$2" in
-                               "") echo "Missing number of simultaneous streams" ; exit 1 ;;
-                               *) MAXSESSIONS=$2 ; shift 2 ;;
-                       esac ;;
+                       is_number $2 || { echo "Missing number of streams" ; exit 1 ; }
+                       MAXSTREAMS=$2 ; shift 2 ;;
                --) shift ; break ;;
-               *) echo "Usage: speedtest-netperf.sh [ -s | -c ] [-4 | -6] [ -H netperf-server ] [ -t duration ] [ -p host-to-ping ] [ -n simultaneous-sessions ]" ; exit 1 ;;
+               *) print_usage ; exit 1 ;;
        esac
 done
 
+# Extra argument validations
+
+if [ $TESTIDLE -eq "0" ] && [ $TESTSPEED -eq "0" ]; then
+       echo "Please select an idle latency test and/or speed test:"
+       print_usage ; exit 1
+fi
+
 # Check dependencies
 
 if ! netperf -V >/dev/null 2>&1; then
        echo "Missing netperf program, please install" ; exit 1
 fi
 
-# Start the main test
+# Catch a Ctl-C and stop background netperf, CPU stats, pinging and print_dots
+trap kill_background_and_exit HUP INT TERM
+
+# Start the main tests
 
 DATE=$(date "+%Y-%m-%d %H:%M:%S")
-echo "$DATE Starting speedtest for $TESTDUR seconds per transfer session."
-echo "Measure speed to $TESTHOST (IPv${TESTPROTO#-}) while pinging $PINGHOST."
-echo -n "Download and upload sessions are "
-[ "$TESTSEQ " -eq "1" ] && echo -n "sequential," || echo -n "concurrent,"
-echo " each with $MAXSESSIONS simultaneous streams."
+echo -n "$DATE Begin test with "
+[ $TESTIDLE -eq "1" ] && echo -n "$IDLEDUR-second ping"
+[ $(($TESTIDLE * $TESTSPEED)) -ne "0" ] && echo -n ", "
+[ $TESTSPEED -ne "0" ] && echo -n "$SPEEDDUR-second transfer"
+echo " sessions."
+
+if [ $TESTIDLE -eq "1" ]; then
+       echo "Measure idle latency by pinging $PINGHOST (IPv${TESTPROTO#-})."
+       measure_idle
+       echo
+fi
 
-# Catch a Ctl-C and stop background netperf, CPU stats, pinging and print_dots
-trap kill_background_and_exit HUP INT TERM
+if [ $TESTSPEED -ne "0" ]; then
+       echo "Measure speed to $TESTHOST (IPv${TESTPROTO#-}) while pinging $PINGHOST."
+       echo -n "Download and upload sessions are "
+       [ "$TESTSPEED" -eq "1" ] && echo -n "sequential," || echo -n "concurrent,"
+       echo " each with $MAXSTREAMS simultaneous streams."
 
-if [ $TESTSEQ -eq "1" ]; then
-       measure_direction "Download"
-       measure_direction "Upload"
-else
-       measure_direction "Bidirectional"
+       if [ $TESTSPEED -eq "1" ]; then
+               measure_direction "Download"
+               measure_direction "Upload"
+       else
+               measure_direction "Bidirectional"
+       fi
 fi