Recommended router for asymmetric gigabit speed

There you go, bringing facts about the real world into the conversation, Heretic!

4 Likes

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
2 Likes

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)