There you go, bringing facts about the real world into the conversation, Heretic!
I made a few additional tweaks to my configuration this morning and got even better results. I'm really impressed!
- CPU overclocked to 2GHz
- Software offloading disabled
- Packet steering enabled
- irqbalance installed and enabled (I feel like there may still be some room for improvement here; cores 2 and 3 are still lightly loaded during Waveform tests though this may be by design/due to hardware limitations)
SQM off
Ookla speedtest: https://www.speedtest.net/result/12507930601
- 2ms ping
- 927.14 Mbps down
- 928.83 Mbps up
SQM on (piece_of_cake @ 850 Mbps down, 850 Mbps up)
Ookla speedtest: https://www.speedtest.net/result/12507942606
- 2ms ping
- 794.79 Mbps down
- 800.76 Mbps up
Waveform: https://www.waveform.com/tools/bufferbloat?test-id=e4d378fc-2024-46a9-adac-bdfb1c3c71f0
- 14 ms unloaded latency
- +0 ms download active
- +0 ms upload active
- 808.3 Mbps down
- 799.1 Mbps up
I am very frustrated with tests that don't test up and down simultaneously.
I am with you on that. So I hacked @richb-hanover-priv's betterspeedtest to use OOkla's speedtest.nat as load generator:
#!/bin/sh
# use Ookla's speedtest.net infrastructure to generate loads and ICMP echos ("pings")
# to concurrently assess the latency under load behaviour of a link.
# This is a crude hack around the issue that netperf/iperf[|2|3] reflectors are
# really rare on the internet, while speedtest.net nodes are rather ubiquitious...
# to implement high resolution pings this should be called under "sudo"
# betterspeedtest.sh - Script to simulate http://speedtest.net
# Start pinging, then initiate a download, let it finish, then start an upload
# Output the measured transfer rates and the resulting ping latency
# It's better than 'speedtest.net' because it measures latency *while* measuring the speed.
# Usage: sh betterspeedtest.sh [-4 -6] [ -H netperf-server ] [ -t duration ] [ -p host-to-ping ] [ -n simultaneous-streams ]
# Options: If options are present:
#
# -H | --host: DNS or Address of a netperf server (default - netperf.bufferbloat.net)
# 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 for how long each direction's test should run - (default - 60 seconds)
# -p | --ping: Host to ping to measure latency (default - gstatic.com)
# -n | --number: Number of simultaneous sessions ()
# Copyright (c) 2014 - Rich Brown rich.brown@blueberryhillsoftware.com
# GPLv2
# Summarize the contents of the ping's output file to show min, avg, median, max, etc.
# input parameter ($1) file contains the output of the ping command
summarize_pings() {
# Process the ping times, and summarize the results
# grep to keep lines that have "time=", then sed to isolate the time stamps, and sort them
# awk builds an array of those values, and prints first & last (which are min, max)
# and computes average.
# If the number of samples is >= 10, also computes median, and 10th and 90th percentile readings
sed 's/^.*time=\([^ ]*\) ms/\1/' < $1 | grep -v "PING" | sort -n | \
awk 'BEGIN {numdrops=0; numrows=0;} \
{ \
if ( $0 ~ /timeout/ ) { \
numdrops += 1; \
} else { \
numrows += 1; \
arr[numrows]=$1; sum+=$1; \
} \
} \
END { \
pc10="-"; pc90="-"; med="-"; \
if (numrows == 0) {numrows=1} \
if (numrows>=10) \
{ ix=int(numrows/10); pc10=arr[ix]; ix=int(numrows*9/10);pc90=arr[ix]; \
if (numrows%2==1) med=arr[(numrows+1)/2]; else med=(arr[numrows/2]); \
}; \
pktloss = numdrops/(numdrops+numrows) * 100; \
printf(" Latency: (in msec, %d pings, %4.2f%% packet loss)\n Min: %4.3f \n 10pct: %4.3f \n Median: %4.3f \n Avg: %4.3f \n 90pct: %4.3f \n Max: %4.3f\n", numrows, pktloss, arr[1], pc10, med, sum/numrows, pc90, arr[numrows] )\
}'
}
# Print a line of dots as a progress indicator.
print_dots() {
while : ; do
printf "."
sleep 1s
done
}
# Stop the current print_dots() process
kill_dots() {
# echo "Pings: $ping_pid Dots: $dots_pid"
kill -9 $dots_pid
wait $dots_pid 2>/dev/null
dots_pid=0
}
# Stop the current ping process
kill_pings() {
# echo "Pings: $ping_pid Dots: $dots_pid"
kill -9 $ping_pid
wait $ping_pid 2>/dev/null
ping_pid=0
}
# Stop the current pings and dots, and exit
# ping command catches (and handles) first Ctrl-C, so you have to hit it again...
kill_pings_and_dots_and_exit() {
kill_dots
echo "\nStopped"
exit 1
}
# ------------ Measure speed and ping latency for one direction ----------------
#
# Call measure_direction() with single parameter - "Download" or " Upload"
# The function gets other info from globals determined from command-line arguments
measure_direction() {
# Create temp files
PINGFILE=$( mktemp /tmp/ping_output.XXXXXX ) || exit 1
SPEEDFILE=$( mktemp /tmp/ooklaspeedtest_output.XXXXXX ) || exit 1
DIRECTION=$1
# Start dots
print_dots &
dots_pid=$!
# echo "Dots PID: $dots_pid"
# Start Ping
if [ $TESTPROTO -eq "-4" ]
then
ping -i 0.2 $PINGHOST > $PINGFILE &
else
ping6 -i 0.2 $PINGHOST > $PINGFILE &
fi
ping_pid=$!
# echo "Ping PID: $ping_pid"
OOKLA_DIR_ARG=""
# Start netperf with the proper direction
if [ "$DIRECTION" = "Download" ]; then
OOKLA_DIR_ARG="--no-upload"
fi
if [ "$DIRECTION" = "Upload" ]; then
OOKLA_DIR_ARG="--no-download"
fi
if [ "$DIRECTION" = "Both" ]; then
OOKLA_DIR_ARG=""
fi
if [ "$MAXSESSIONS" -eq "1" ]; then
OOKLA_SESS_ARG="--single"
else
# otherwise default to whatever ookla uses, the cli app has no control for this
OOKLA_SESS_ARG=""
fi
# # Start $MAXSESSIONS datastreams between netperf client and the netperf server
# # netperf writes the sole output value (in Mbps) to stdout when completed
# for i in $( seq $MAXSESSIONS )
# do
# netperf $TESTPROTO -H $TESTHOST -t $dir -l $TESTDUR -v 0 -P 0 >> $SPEEDFILE &
# # echo "Starting PID $! params: $TESTPROTO -H $TESTHOST -t $dir -l $TESTDUR -v 0 -P 0 >> $SPEEDFILE"
# done
# Wait until each of the background netperf processes completes
# echo "Process is $$"
# echo `pgrep -P $$ netperf `
if [ $DIRECTION = "Both" ]; then
speedtest --no-upload ${OOKLA_SESS_ARG} >> ${SPEEDFILE} & OOKLA_DOWN_APP_PID=$!
#echo "Started PID ${OOKLA_DOWN_APP_PID} params: $TESTPROTO -H $TESTHOST -t $dir -l $TESTDUR -v 0 -P 0 >> $SPEEDFILE"
echo "Started PID ${OOKLA_DOWN_APP_PID} command: speedtest-cli --no-upload ${OOKLA_SESS_ARG} >> ${SPEEDFILE}"
speedtest --no-download ${OOKLA_SESS_ARG} >> ${SPEEDFILE} & OOKLA_UP_APP_PID=$!
#echo "Started PID ${OOKLA_UP_APP_PID} params: $TESTPROTO -H $TESTHOST -t $dir -l $TESTDUR -v 0 -P 0 >> $SPEEDFILE"
echo "Started PID ${OOKLA_UP_APP_PID} command: speedtest-cli --no-download ${OOKLA_SESS_ARG} >> ${SPEEDFILE}"
wait ${OOKLA_UP_APP_PID}
wait ${OOKLA_DOWN_APP_PID}
else
speedtest ${OOKLA_DIR_ARG} ${OOKLA_SESS_ARG} >> ${SPEEDFILE} & OOKLA_APP_PID=$!
#echo "Started PID ${OOKLA_APP_PID} params: $TESTPROTO -H $TESTHOST -t $dir -l $TESTDUR -v 0 -P 0 >> $SPEEDFILE"
echo "Started PID ${OOKLA_APP_PID} command: speedtest-cli ${OOKLA_DIR_ARG} ${OOKLA_SESS_ARG} >> ${SPEEDFILE}"
# this will all run inside a Python instance which will be hard to catch so rather collect the PID directly
wait ${OOKLA_APP_PID}
fi
if [ $DIRECTION = "Both" ]; then
echo ""
echo $( grep -e Download $SPEEDFILE )
echo $( grep -e Upload $SPEEDFILE )
else
# Print TCP Download speed
echo ""
#echo " $1: " $( awk '{s+=$1} END {print s}' $SPEEDFILE ) Mbps
#echo " $1: " $( grep -e $1 $SPEEDFILE ) Mbps
echo $( grep -e $1 $SPEEDFILE )
fi
# When netperf completes, stop the dots and the pings
kill_pings
kill_dots
# Summarize the ping data
summarize_pings $PINGFILE
#cat $PINGFILE
rm $PINGFILE
rm $SPEEDFILE
}
# ------- Start of the main routine --------
# Usage: sh betterspeedtest.sh [ -4 -6 ] [ -H netperf-server ] [ -t duration ] [ -p host-to-ping ] [ -n simultaneous-sessions ]
# “H” and “host” DNS or IP address of the netperf server host (default: netperf.bufferbloat.net)
# “t” and “time” Time to run the test in each direction (default: 60 seconds)
# “p” and “ping” Host to ping for latency measurements (default: gstatic.com)
# "n" and "number" Number of simultaneous upload or download sessions (default: 5 sessions;
# 5 sessions chosen empirically because total didn't increase much after that number)
# set an initial values for defaults
TESTHOST="netperf.bufferbloat.net"
TESTDUR="60"
PINGHOST="gstatic.com"
MAXSESSIONS="5"
TESTPROTO="-4"
# read the options
# extract options and their arguments into variables.
while [ $# -gt 0 ]
do
case "$1" in
-4|-6) TESTPROTO=$1 ; shift 1 ;;
-H|--host)
case "$2" in
"") echo "Missing hostname" ; exit 1 ;;
*) TESTHOST=$2 ; shift 2 ;;
esac ;;
-t|--time)
case "$2" in
"") echo "Missing duration" ; exit 1 ;;
*) TESTDUR=$2 ; shift 2 ;;
esac ;;
-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 sessions" ; exit 1 ;;
*) MAXSESSIONS=$2 ; shift 2 ;;
esac ;;
--) shift ; break ;;
*) echo "Usage: sh betterspeedtest.sh [-4 -6] [ -H netperf-server ] [ -t duration ] [ -p host-to-ping ] [ -n simultaneous-sessions ]" ; exit 1 ;;
esac
done
# Start the main test
if [ $TESTPROTO -eq "-4" ]
then
PROTO="ipv4"
else
PROTO="ipv6"
fi
DATE=$( date "+%Y-%m-%d %H:%M:%S" )
echo "$DATE Testing against $TESTHOST ($PROTO) with $MAXSESSIONS simultaneous sessions while pinging $PINGHOST ($TESTDUR seconds in each direction)"
# Catch a Ctl-C and stop the pinging and the print_dots
trap kill_pings_and_dots_and_exit HUP INT TERM
measure_direction "Download"
measure_direction "Upload"
measure_direction "Both"
exit 0
which requires a specific python speedtest-cli variant (will not work with the official Ookla CLI as far as I can tell since that does not allow to selectively select the direction to test).
But from long time exposure to RRUL, I actually think that there is another test worth doing, saturate one direction and then "pump" the other (something like 100% load, 50% load 0% load, 100%, 50%, 0%, ... for a few seconds each, then repeat with the constant and pumped load direction reversed) that would also test the transients of load shifting (which might be more realistic that continously loading both directions of a link)