CAKE w/ Adaptive Bandwidth [October 2021 to September 2022]

Looking much better now with:

#!/bin/sh

max_ul_rate=35000
min_ul_rate=25000

max_dl_rate=60000
min_dl_rate=20000

read -d '' reflectors << EOF
1.1.1.1
8.8.8.8
EOF

alpha=0.1

ratio_adjust_down=0.25
ratio_adjust_up=0.125

max_delta_RTT=20

rx_bytes_path="/sys/class/net/wan/statistics/rx_bytes"
tx_bytes_path="/sys/class/net/wan/statistics/tx_bytes"

no_reflectors=$(echo "$reflectors" | wc -l)

rx_load_thresh=$(echo "scale=4; $min_dl_rate/2"|bc)
tx_load_thresh=$(echo "scale=4; $min_ul_rate/2"|bc)

RTTs=$(mktemp)

function get_avg_RTT {

for reflector in $reflectors;
do
        echo $(ping -i 0.2 -c 10 $reflector | tail -1 | awk '{print $4}' | cut -d '/' -f 2) >> $RTTs&
done
wait
avg_RTT=$(echo $(cat $RTTs) | awk '{ total += $2; count++ } END { print total/count }')
> $RTTs
}

function update_rates {
        t_start=$(date +%s)
        get_avg_RTT
        delta_RTT=$(echo "scale=4; $avg_RTT - $prev_avg_RTT" | bc)
        prev_avg_RTT=$(echo "scale=4; (1-$alpha)*$prev_avg_RTT+$alpha*$avg_RTT" | bc)

        if [ $(echo "$delta_RTT > $max_delta_RTT" | bc -l) -eq 1 ]; then

                cur_dl_rate=$(echo "scale=4; $cur_dl_rate-$ratio_adjust_down*($max_dl_rate-$min_dl_rate)" | bc)
                cur_ul_rate=$(echo "scale=4; $cur_ul_rate-($max_ul_rate-$min_ul_rate)/4" | bc)
        fi
        cur_rx_bytes=$(cat $rx_bytes_path)
        cur_tx_bytes=$(cat $tx_bytes_path)
        t_cur_bytes=$(date +%s)

        rx_load=$(echo "scale=4; (8/1000)*(($cur_rx_bytes-$prev_rx_bytes)/($t_cur_bytes-$t_prev_bytes))"|bc)
        tx_load=$(echo "scale=4; (8/1000)*(($cur_tx_bytes-$prev_tx_bytes)/($t_cur_bytes-$t_prev_bytes))"|bc)

        t_prev_bytes=$t_cur_bytes
        prev_rx_bytes=$cur_rx_bytes
        prev_tx_bytes=$cur_tx_bytes

        if [ $(echo "$delta_RTT < $max_delta_RTT && $rx_load > $rx_load_thresh" |bc) -eq 1 ]; then
                cur_dl_rate=$(echo "scale=4; $cur_dl_rate + $ratio_adjust_up*($max_dl_rate-$min_dl_rate)"|bc)
        fi

        if [ $(echo "$delta_RTT < $max_delta_RTT && $tx_load > $tx_load_thresh" |bc) -eq 1 ]; then
                cur_ul_rate=$(echo "scale=4; $cur_ul_rate + $ratio_adjust_up*($max_ul_rate-$min_ul_rate)"|bc)
        fi

        if [ $(echo "$delta_RTT < $max_delta_RTT && $rx_load < $rx_load_thresh" |bc) -eq 1 ]; then
                cur_dl_rate=$(echo "scale=4; $cur_dl_rate - $ratio_adjust_down*($max_dl_rate-$min_dl_rate)"|bc)
        fi

        if [ $(echo "$delta_RTT < $max_delta_RTT && $tx_load < $tx_load_thresh" |bc) -eq 1 ]; then
                cur_ul_rate=$(echo "scale=4; $cur_ul_rate - $ratio_adjust_down*($max_ul_rate-$min_ul_rate)"|bc)
        fi

        if [ $(echo "$cur_dl_rate<$min_dl_rate" | bc) -eq 1 ]; then
                cur_dl_rate=$min_dl_rate;
        fi

        if [ $(echo "$cur_ul_rate<$min_ul_rate" | bc) -eq 1 ]; then
                cur_ul_rate=$min_ul_rate;
        fi

        t_end=$(date +%s)

        sleep $(echo "5-($t_start-$t_end)"|bc)

        echo "rx_load= $rx_load; tx_load=$tx_load; delta_RTT= $delta_RTT; cur_dl_rate= $cur_dl_rate; cur_dl_rate= $cur_ul_rate"
}


get_avg_RTT

prev_avg_RTT=$avg_RTT;

cur_dl_rate=$min_dl_rate
cur_ul_rate=$min_ul_rate

t_prev_bytes=$(date +%s)

prev_rx_bytes=$(cat $rx_bytes_path)
prev_tx_bytes=$(cat $tx_bytes_path)

while true
do
update_rates
done

Output (scales up with dl / ul loading and then back down):

rx_load= 17.2800; tx_load=30.8880; delta_RTT= .208; cur_dl_rate= 20000; cur_dl_rate= 25000
rx_load= 350.3448; tx_load=47.1093; delta_RTT= .8182; cur_dl_rate= 20000; cur_dl_rate= 25000
rx_load= 400.0693; tx_load=36.8248; delta_RTT= -2.1526; cur_dl_rate= 20000; cur_dl_rate= 25000
rx_load= 1194.7200; tx_load=53.5982; delta_RTT= .0027; cur_dl_rate= 20000; cur_dl_rate= 25000
rx_load= 893.0151; tx_load=58.5226; delta_RTT= 1.2345; cur_dl_rate= 20000; cur_dl_rate= 25000
rx_load= 27741.8177; tx_load=2477.0124; delta_RTT= 2.5511; cur_dl_rate= 25000.000; cur_dl_rate= 25000
rx_load= 27224.9297; tx_load=2415.0524; delta_RTT= 1.5780; cur_dl_rate= 30000.000; cur_dl_rate= 25000
rx_load= 2399.3724; tx_load=19651.2533; delta_RTT= 1.2822; cur_dl_rate= 20000.000; cur_dl_rate= 26250.000
rx_load= 636.4060; tx_load=5488.1810; delta_RTT= -.4020; cur_dl_rate= 20000; cur_dl_rate= 25000
rx_load= 401.8080; tx_load=31.2533; delta_RTT= -.7318; cur_dl_rate= 20000; cur_dl_rate= 25000

By the way with:

rx_load=$(echo "scale=4; (8/1000)*(($cur_rx_bytes-$prev_rx_bytes)/($t_cur_bytes-$t_prev_bytes))"|bc)

should I divide by 1000 or 1024?

Also general question. Concerning Netflix and Prime sending periodic spikes. Is there a situation in which I would want to increase download to offer more bandwidth for those spikes but the load will be less than half my minimum over the sample duration? Or if Netflix/Prime need more bandwidth do they just increase the width and/or frequency of those spikes such that it will still over my sample duration eat up the load needed to trigger an increase? Obviously so long as I am satisfying the Netflix/Prime 4K demand there is no need to increase.

I guess I am trying to work out if just working with cumulative bandwidth over the sample duration is sufficient or I should also be reacting to things hitting the ceiling in a periodic way. Hmm.

I think for solid file downloads / OneDrive uploads etc the load increases will trigger as they should.

Will test properly later with the actual tc qdisc calls.

So I would move the sleep into the main loop, just after the call to tc to update the rates.

Traditionally in networking, data volume has been measured in true si-units, so K being 1000, not 2^10=1024, but in the end that is your choice; as long as you convert and use the units consciously both should work. (Personally, having 10 fingers and not just 2, I like base10 better than base2, so would stick to KBps instead of KiBps, heck, I probably would convert to kilo-bit-per-second so that the values are in the same unit as SQM's rate settings, which are in Kbps).

You need to measure that, but you can always fine-tune your xx_load_thresh values and or the time you run the loop (BTW, as you probably know, making the period smaller than the expected ping train duration for all targets makes the sleep call superflous) :wink:

I do not know, but I would expect that if they can not refill their buffers X milliseconds before that buffer runs out, they are going to request a lower sending rate/lower quality, how that relates to your parameters I do not know.

Yes, but the devil is in the details, so you would need to know the buffer low water-marks Netflix uses to decide when to scale quality up or down... (which with variable rate codecs might not even be invariant over time).

I would guess that this is equivalent to just lowering your load_thresholds, at least as first approximation, no?

+1;

1 Like

Hey @moeller0, so far so good with the tc qdisc calls. Looking extremely promising to me. Nice for me to see how high my LTE bandwidth can go.

Here is the latest code:

#!/bin/sh

max_ul_rate=35000
min_ul_rate=25000

max_dl_rate=60000
min_dl_rate=20000

read -d '' reflectors << EOF
1.1.1.1
8.8.8.8
EOF

alpha=0.1

ratio_adjust_down=0.25
ratio_adjust_up=0.125

max_delta_RTT=20

rx_bytes_path="/sys/class/net/wan/statistics/rx_bytes"
tx_bytes_path="/sys/class/net/wan/statistics/tx_bytes"

no_reflectors=$(echo "$reflectors" | wc -l)

rx_load_thresh=$(echo "scale=4; $min_dl_rate/2"|bc)
tx_load_thresh=$(echo "scale=4; $min_ul_rate/2"|bc)

RTTs=$(mktemp)

function get_avg_RTT {

for reflector in $reflectors;
do
        echo $(ping -i 0.2 -c 10 $reflector | tail -1 | awk '{print $4}' | cut -d '/' -f 2) >> $RTTs&
done
wait
avg_RTT=$(echo $(cat $RTTs) | awk '{ total += $2; count++ } END { print total/count }')
> $RTTs
}

function update_rates {
        get_avg_RTT
        delta_RTT=$(echo "scale=4; $avg_RTT - $prev_avg_RTT" | bc)
        prev_avg_RTT=$(echo "scale=4; (1-$alpha)*$prev_avg_RTT+$alpha*$avg_RTT" | bc)

        if [ $(echo "$delta_RTT > $max_delta_RTT" | bc -l) -eq 1 ]; then

                cur_dl_rate=$(echo "scale=4; $cur_dl_rate-$ratio_adjust_down*($max_dl_rate-$min_dl_rate)" | bc)
                cur_ul_rate=$(echo "scale=4; $cur_ul_rate-($max_ul_rate-$min_ul_rate)/4" | bc)
        fi
        cur_rx_bytes=$(cat $rx_bytes_path)
        cur_tx_bytes=$(cat $tx_bytes_path)
        t_cur_bytes=$(date +%s)

        rx_load=$(echo "scale=4; (8/1000)*(($cur_rx_bytes-$prev_rx_bytes)/($t_cur_bytes-$t_prev_bytes))"|bc)
        tx_load=$(echo "scale=4; (8/1000)*(($cur_tx_bytes-$prev_tx_bytes)/($t_cur_bytes-$t_prev_bytes))"|bc)

        t_prev_bytes=$t_cur_bytes
        prev_rx_bytes=$cur_rx_bytes
        prev_tx_bytes=$cur_tx_bytes

        if [ $(echo "$delta_RTT < $max_delta_RTT && $rx_load > $rx_load_thresh" |bc) -eq 1 ]; then
                cur_dl_rate=$(echo "scale=4; $cur_dl_rate + $ratio_adjust_up*($max_dl_rate-$min_dl_rate)"|bc)
        fi

        if [ $(echo "$delta_RTT < $max_delta_RTT && $tx_load > $tx_load_thresh" |bc) -eq 1 ]; then
                cur_ul_rate=$(echo "scale=4; $cur_ul_rate + $ratio_adjust_up*($max_ul_rate-$min_ul_rate)"|bc)
        fi

        if [ $(echo "$delta_RTT < $max_delta_RTT && $rx_load < $rx_load_thresh" |bc) -eq 1 ]; then
                cur_dl_rate=$(echo "scale=4; $cur_dl_rate - $ratio_adjust_down*($max_dl_rate-$min_dl_rate)"|bc)
        fi

        if [ $(echo "$delta_RTT < $max_delta_RTT && $tx_load < $tx_load_thresh" |bc) -eq 1 ]; then
                cur_ul_rate=$(echo "scale=4; $cur_ul_rate - $ratio_adjust_down*($max_ul_rate-$min_ul_rate)"|bc)
        fi

        if [ $(echo "$cur_dl_rate<$min_dl_rate" | bc) -eq 1 ]; then
                cur_dl_rate=$min_dl_rate;
        fi

        if [ $(echo "$cur_ul_rate<$min_ul_rate" | bc) -eq 1 ]; then
                cur_ul_rate=$min_ul_rate;
        fi

        echo -e "rx_load= $rx_load;\ttx_load=$tx_load;\tdelta_RTT= $delta_RTT;\tcur_dl_rate= $cur_dl_rate;\tcur_dl_rate= $cur_ul_rate"
}


get_avg_RTT

prev_avg_RTT=$avg_RTT;

cur_dl_rate=$min_dl_rate
cur_ul_rate=$min_ul_rate

t_prev_bytes=$(date +%s)

prev_rx_bytes=$(cat $rx_bytes_path)
prev_tx_bytes=$(cat $tx_bytes_path)

while true
do
        t_start=$(date +%s)
        update_rates
        tc qdisc change root dev wan cake bandwidth "$cur_ul_rate"Kbit
        tc qdisc change root dev veth-lan cake bandwidth "$cur_dl_rate"Kbit
        t_end=$(date +%s)
        sleep $(echo "5-($t_start-$t_end)"|bc)
done

Check out these results with active download/upload. The rates correctly increase until my connection gets saturated and bufferbloat is detected. And with no load the rates decay back down.

root@OpenWrt:~# ./sqm-autorate
rx_load= 6993.6840;     tx_load=163.1000;       delta_RTT= 3.331;       cur_dl_rate= 20000;     cur_dl_rate= 25000
rx_load= 7848.2800;     tx_load=274.5137;       delta_RTT= 4.1949;      cur_dl_rate= 20000;     cur_dl_rate= 25000
rx_load= 7059.6444;     tx_load=180.3644;       delta_RTT= 2.6265;      cur_dl_rate= 20000;     cur_dl_rate= 25000
rx_load= 10516.4177;    tx_load=263.3040;       delta_RTT= 4.3119;      cur_dl_rate= 25000.000; cur_dl_rate= 25000
rx_load= 4324.5084;     tx_load=141.6666;       delta_RTT= -2.6302;     cur_dl_rate= 20000;     cur_dl_rate= 25000
rx_load= 12492.8355;    tx_load=301.7822;       delta_RTT= -4.5931;     cur_dl_rate= 25000.000; cur_dl_rate= 25000
rx_load= 6121.8053;     tx_load=150.0320;       delta_RTT= -3.8677;     cur_dl_rate= 20000;     cur_dl_rate= 25000
rx_load= 5260.9750;     tx_load=232.9810;       delta_RTT= -.4469;      cur_dl_rate= 20000;     cur_dl_rate= 25000
rx_load= 8336.3884;     tx_load=207.5404;       delta_RTT= 22.6068;     cur_dl_rate= 20000;     cur_dl_rate= 25000
rx_load= 6498.4755;     tx_load=182.5822;       delta_RTT= -1.1468;     cur_dl_rate= 20000;     cur_dl_rate= 25000
rx_load= 8867.5890;     tx_load=216.2500;       delta_RTT= -3.5871;     cur_dl_rate= 20000;     cur_dl_rate= 25000
rx_load= 8394.9840;     tx_load=211.5911;       delta_RTT= 1.5597;      cur_dl_rate= 20000;     cur_dl_rate= 25000
rx_load= 10130.5448;    tx_load=248.8560;       delta_RTT= -5.0252;     cur_dl_rate= 25000.000; cur_dl_rate= 25000
rx_load= 2811.9688;     tx_load=73.9804;        delta_RTT= -1.7946;     cur_dl_rate= 20000;     cur_dl_rate= 25000
rx_load= 6784.8373;     tx_load=302.6168;       delta_RTT= -.9931;      cur_dl_rate= 20000;     cur_dl_rate= 25000
rx_load= 8579.4586;     tx_load=280.3528;       delta_RTT= 1.4003;      cur_dl_rate= 20000;     cur_dl_rate= 25000
rx_load= 9224.5715;     tx_load=340.3128;       delta_RTT= 1.1803;      cur_dl_rate= 20000;     cur_dl_rate= 25000
rx_load= 15105.0044;    tx_load=727.9120;       delta_RTT= -1.7817;     cur_dl_rate= 25000.000; cur_dl_rate= 25000
rx_load= 19799.4026;    tx_load=1180.8240;      delta_RTT= -1.6905;     cur_dl_rate= 30000.000; cur_dl_rate= 25000
rx_load= 20079.8382;    tx_load=1005.5004;      delta_RTT= -.9204;      cur_dl_rate= 35000.000; cur_dl_rate= 25000
rx_load= 27861.5591;    tx_load=1159.9093;      delta_RTT= -.2203;      cur_dl_rate= 40000.000; cur_dl_rate= 25000
rx_load= 26286.6835;    tx_load=1109.1128;      delta_RTT= .5058;       cur_dl_rate= 45000.000; cur_dl_rate= 25000
rx_load= 30666.7137;    tx_load=1053.0204;      delta_RTT= 5.3523;      cur_dl_rate= 50000.000; cur_dl_rate= 25000
rx_load= 40704.8995;    tx_load=1944.7128;      delta_RTT= -3.3089;     cur_dl_rate= 55000.000; cur_dl_rate= 25000
rx_load= 41949.2186;    tx_load=2028.3102;      delta_RTT= 2.1890;      cur_dl_rate= 60000.000; cur_dl_rate= 25000
rx_load= 38838.2675;    tx_load=1355.2533;      delta_RTT= -2.2309;     cur_dl_rate= 65000.000; cur_dl_rate= 25000
rx_load= 53037.8933;    tx_load=1008.2364;      delta_RTT= 41.8702;     cur_dl_rate= 55000.000; cur_dl_rate= 25000
rx_load= 36341.6835;    tx_load=1506.5013;      delta_RTT= -5.7218;     cur_dl_rate= 60000.000; cur_dl_rate= 25000
rx_load= 50998.1413;    tx_load=2537.6426;      delta_RTT= -2.5996;     cur_dl_rate= 65000.000; cur_dl_rate= 25000
rx_load= 46356.5991;    tx_load=2122.6551;      delta_RTT= 1.7704;      cur_dl_rate= 70000.000; cur_dl_rate= 25000
rx_load= 54512.5168;    tx_load=1884.7568;      delta_RTT= 84.6544;     cur_dl_rate= 60000.000; cur_dl_rate= 25000
rx_load= 52568.9384;    tx_load=2635.8208;      delta_RTT= -14.0380;    cur_dl_rate= 65000.000; cur_dl_rate= 25000
rx_load= 40888.5893;    tx_load=1935.3840;      delta_RTT= -10.7782;    cur_dl_rate= 70000.000; cur_dl_rate= 25000
rx_load= 57954.4462;    tx_load=1585.3413;      delta_RTT= 65.4327;     cur_dl_rate= 60000.000; cur_dl_rate= 25000
rx_load= 53888.6124;    tx_load=2744.5422;      delta_RTT= -1.8445;     cur_dl_rate= 65000.000; cur_dl_rate= 25000
rx_load= 48193.4515;    tx_load=2026.7342;      delta_RTT= 25.5330;     cur_dl_rate= 55000.000; cur_dl_rate= 25000
rx_load= 46945.5093;    tx_load=2351.0426;      delta_RTT= -16.0083;    cur_dl_rate= 60000.000; cur_dl_rate= 25000
rx_load= 35885.6222;    tx_load=1550.7742;      delta_RTT= -15.3184;    cur_dl_rate= 65000.000; cur_dl_rate= 25000
rx_load= 55087.7564;    tx_load=2253.4844;      delta_RTT= -14.4765;    cur_dl_rate= 70000.000; cur_dl_rate= 25000
rx_load= 58876.1315;    tx_load=2779.8400;      delta_RTT= 69.5712;     cur_dl_rate= 60000.000; cur_dl_rate= 25000
rx_load= 50486.5715;    tx_load=2619.3475;      delta_RTT= 1.9851;      cur_dl_rate= 65000.000; cur_dl_rate= 25000
rx_load= 43474.0400;    tx_load=1187.3644;      delta_RTT= 36.5816;     cur_dl_rate= 55000.000; cur_dl_rate= 25000
rx_load= 47645.9537;    tx_load=2172.3288;      delta_RTT= -19.2765;    cur_dl_rate= 60000.000; cur_dl_rate= 25000
rx_load= 45005.1626;    tx_load=2112.0888;      delta_RTT= -17.9378;    cur_dl_rate= 65000.000; cur_dl_rate= 25000
rx_load= 54008.2284;    tx_load=2417.4720;      delta_RTT= 60.7320;     cur_dl_rate= 55000.000; cur_dl_rate= 25000
rx_load= 43954.1137;    tx_load=2152.2817;      delta_RTT= -28.9922;    cur_dl_rate= 60000.000; cur_dl_rate= 25000
rx_load= 47306.1217;    tx_load=2396.1084;      delta_RTT= -19.3139;    cur_dl_rate= 65000.000; cur_dl_rate= 25000
rx_load= 43914.2960;    tx_load=1978.5795;      delta_RTT= -16.9885;    cur_dl_rate= 70000.000; cur_dl_rate= 25000
rx_load= 57586.5644;    tx_load=2675.7155;      delta_RTT= 52.7434;     cur_dl_rate= 60000.000; cur_dl_rate= 25000
rx_load= 22817.6968;    tx_load=1015.0773;      delta_RTT= -23.1499;    cur_dl_rate= 65000.000; cur_dl_rate= 25000
rx_load= 6753.8390;     tx_load=304.8310;       delta_RTT= -23.8119;    cur_dl_rate= 55000.000; cur_dl_rate= 25000
rx_load= 17863.0400;    tx_load=1029.3288;      delta_RTT= -15.9857;    cur_dl_rate= 60000.000; cur_dl_rate= 25000
rx_load= 43512.2568;    tx_load=2730.0124;      delta_RTT= -6.6541;     cur_dl_rate= 65000.000; cur_dl_rate= 25000
rx_load= 49689.4552;    tx_load=2290.6378;      delta_RTT= 67.1314;     cur_dl_rate= 55000.000; cur_dl_rate= 25000
rx_load= 43511.5066;    tx_load=2666.7928;      delta_RTT= -26.9797;    cur_dl_rate= 60000.000; cur_dl_rate= 25000
rx_load= 31731.4986;    tx_load=7251.0897;      delta_RTT= -23.3297;    cur_dl_rate= 65000.000; cur_dl_rate= 25000
rx_load= 11309.6871;    tx_load=19205.2693;     delta_RTT= -11.1317;    cur_dl_rate= 70000.000; cur_dl_rate= 26250.000
rx_load= 5767.4693;     tx_load=21337.3448;     delta_RTT= -21.0055;    cur_dl_rate= 60000.000; cur_dl_rate= 27500.000
rx_load= 9143.2320;     tx_load=13066.2542;     delta_RTT= -21.5169;    cur_dl_rate= 50000.000; cur_dl_rate= 28750.000

Do you suppose the ul and dl change thresholds should be based on the present bandwidth? So only increase if greater than 50% current bandwidth? And vice versa.

yeah, I assumed that was the whole plan. 50 percent of some theoretical max isn't very relevant when interference is making the actual max say 20% of that theoretical max for example.

2 Likes

@moeller0, @dlakelan - I suppose the below indicates alpha needs to be reduced? Because the prev_avg_RTT seems to creep up too much. My typical RTT is around 50ms. Sorry the formatting is horrible.

root@OpenWrt:~# ./sqm-autorate
rx_load= 19.69; tx_load= 32.56; avg_RTT= 47.46; prev_avg_RTT= 48.91;    delta_RTT= -1.60;       cur_dl_rate= 20000.00;  cur_ul_rate= 25000.00
rx_load= 9.81;  tx_load= 28.64; avg_RTT= 50.15; prev_avg_RTT= 49.03;    delta_RTT= 1.24;        cur_dl_rate= 20000.00;  cur_ul_rate= 25000.00
rx_load= 16.97; tx_load= 16.64; avg_RTT= 48.47; prev_avg_RTT= 48.98;    delta_RTT= -0.56;       cur_dl_rate= 20000.00;  cur_ul_rate= 25000.00
rx_load= 17.34; tx_load= 21.89; avg_RTT= 48.56; prev_avg_RTT= 48.93;    delta_RTT= -0.42;       cur_dl_rate= 20000.00;  cur_ul_rate= 25000.00
rx_load= 7.72;  tx_load= 13.15; avg_RTT= 49.02; prev_avg_RTT= 48.94;    delta_RTT= 0.09;        cur_dl_rate= 20000.00;  cur_ul_rate= 25000.00
rx_load= 16.54; tx_load= 19.09; avg_RTT= 47.92; prev_avg_RTT= 48.84;    delta_RTT= -1.02;       cur_dl_rate= 20000.00;  cur_ul_rate= 25000.00
rx_load= 19.16; tx_load= 32.43; avg_RTT= 48.55; prev_avg_RTT= 48.81;    delta_RTT= -0.29;       cur_dl_rate= 20000.00;  cur_ul_rate= 25000.00
rx_load= 28.98; tx_load= 48.97; avg_RTT= 48.33; prev_avg_RTT= 48.76;    delta_RTT= -0.48;       cur_dl_rate= 20000.00;  cur_ul_rate= 25000.00
rx_load= 21.43; tx_load= 50.20; avg_RTT= 47.69; prev_avg_RTT= 48.66;    delta_RTT= -1.08;       cur_dl_rate= 20000.00;  cur_ul_rate= 25000.00
rx_load= 55.70; tx_load= 60.48; avg_RTT= 48.15; prev_avg_RTT= 48.60;    delta_RTT= -0.51;       cur_dl_rate= 20000.00;  cur_ul_rate= 25000.00
rx_load= 12.18; tx_load= 14.12; avg_RTT= 49.82; prev_avg_RTT= 48.73;    delta_RTT= 1.22;        cur_dl_rate= 20000.00;  cur_ul_rate= 25000.00
rx_load= 9207.00;       tx_load= 219.79;        avg_RTT= 48.61; prev_avg_RTT= 48.71;    delta_RTT= -0.12;       cur_dl_rate= 20000.00;  cur_ul_rate= 25000.00
rx_load= 27455.47;      tx_load= 547.90;        avg_RTT= 47.22; prev_avg_RTT= 48.56;    delta_RTT= -1.50;       cur_dl_rate= 25000.00;  cur_ul_rate= 25000.00
rx_load= 19754.75;      tx_load= 413.45;        avg_RTT= 51.58; prev_avg_RTT= 48.87;    delta_RTT= 3.01;        cur_dl_rate= 30000.00;  cur_ul_rate= 25000.00
rx_load= 24083.18;      tx_load= 554.78;        avg_RTT= 59.46; prev_avg_RTT= 49.92;    delta_RTT= 10.59;       cur_dl_rate= 35000.00;  cur_ul_rate= 25000.00
rx_load= 25727.11;      tx_load= 528.91;        avg_RTT= 58.27; prev_avg_RTT= 50.76;    delta_RTT= 8.35;        cur_dl_rate= 40000.00;  cur_ul_rate= 25000.00
rx_load= 30275.46;      tx_load= 422.94;        avg_RTT= 58.55; prev_avg_RTT= 51.54;    delta_RTT= 7.79;        cur_dl_rate= 45000.00;  cur_ul_rate= 25000.00
rx_load= 22357.67;      tx_load= 307.56;        avg_RTT= 67.94; prev_avg_RTT= 53.18;    delta_RTT= 16.40;       cur_dl_rate= 35000.00;  cur_ul_rate= 25000.00
rx_load= 28386.14;      tx_load= 505.62;        avg_RTT= 75.88; prev_avg_RTT= 55.45;    delta_RTT= 22.70;       cur_dl_rate= 25000.00;  cur_ul_rate= 25000.00
rx_load= 18581.81;      tx_load= 450.32;        avg_RTT= 57.61; prev_avg_RTT= 55.66;    delta_RTT= 2.16;        cur_dl_rate= 30000.00;  cur_ul_rate= 25000.00
rx_load= 21106.89;      tx_load= 323.36;        avg_RTT= 51.83; prev_avg_RTT= 55.28;    delta_RTT= -3.84;       cur_dl_rate= 35000.00;  cur_ul_rate= 25000.00
rx_load= 25822.64;      tx_load= 361.67;        avg_RTT= 54.38; prev_avg_RTT= 55.19;    delta_RTT= -0.90;       cur_dl_rate= 40000.00;  cur_ul_rate= 25000.00
rx_load= 24155.37;      tx_load= 313.11;        avg_RTT= 58.03; prev_avg_RTT= 55.47;    delta_RTT= 2.84;        cur_dl_rate= 45000.00;  cur_ul_rate= 25000.00
rx_load= 30285.38;      tx_load= 404.42;        avg_RTT= 55.78; prev_avg_RTT= 55.50;    delta_RTT= 0.30;        cur_dl_rate= 50000.00;  cur_ul_rate= 25000.00
rx_load= 33529.87;      tx_load= 432.41;        avg_RTT= 83.13; prev_avg_RTT= 58.27;    delta_RTT= 27.62;       cur_dl_rate= 40000.00;  cur_ul_rate= 25000.00
rx_load= 23014.33;      tx_load= 310.02;        avg_RTT= 200.62;        prev_avg_RTT= 72.50;    delta_RTT= 142.35;      cur_dl_rate= 30000.00;  cur_ul_rate= 25000.00
rx_load= 25314.86;      tx_load= 698.09;        avg_RTT= 67.29; prev_avg_RTT= 71.98;    delta_RTT= -5.21;       cur_dl_rate= 35000.00;  cur_ul_rate= 25000.00
rx_load= 24537.29;      tx_load= 649.50;        avg_RTT= 57.33; prev_avg_RTT= 70.52;    delta_RTT= -14.66;      cur_dl_rate= 40000.00;  cur_ul_rate= 25000.00
rx_load= 24767.38;      tx_load= 340.60;        avg_RTT= 61.47; prev_avg_RTT= 69.61;    delta_RTT= -9.05;       cur_dl_rate= 45000.00;  cur_ul_rate= 25000.00
rx_load= 26928.26;      tx_load= 365.13;        avg_RTT= 57.35; prev_avg_RTT= 68.39;    delta_RTT= -12.26;      cur_dl_rate= 50000.00;  cur_ul_rate= 25000.00
rx_load= 29995.26;      tx_load= 382.74;        avg_RTT= 54.26; prev_avg_RTT= 66.97;    delta_RTT= -14.12;      cur_dl_rate= 55000.00;  cur_ul_rate= 25000.00
rx_load= 32114.07;      tx_load= 420.77;        avg_RTT= 60.02; prev_avg_RTT= 66.28;    delta_RTT= -6.96;       cur_dl_rate= 60000.00;  cur_ul_rate= 25000.00
rx_load= 29913.33;      tx_load= 400.25;        avg_RTT= 92.10; prev_avg_RTT= 68.86;    delta_RTT= 25.82;       cur_dl_rate= 50000.00;  cur_ul_rate= 25000.00
rx_load= 30486.73;      tx_load= 435.22;        avg_RTT= 127.00;        prev_avg_RTT= 74.67;    delta_RTT= 58.14;       cur_dl_rate= 40000.00;  cur_ul_rate= 25000.00
rx_load= 25537.69;      tx_load= 329.71;        avg_RTT= 237.55;        prev_avg_RTT= 90.96;    delta_RTT= 162.87;      cur_dl_rate= 30000.00;  cur_ul_rate= 25000.00
rx_load= 24273.92;      tx_load= 413.36;        avg_RTT= 308.82;        prev_avg_RTT= 112.75;   delta_RTT= 217.86;      cur_dl_rate= 20000.00;  cur_ul_rate= 25000.00
rx_load= 15770.20;      tx_load= 585.47;        avg_RTT= 62.09; prev_avg_RTT= 107.68;   delta_RTT= -50.66;      cur_dl_rate= 25000.00;  cur_ul_rate= 25000.00
rx_load= 15018.28;      tx_load= 335.15;        avg_RTT= 53.74; prev_avg_RTT= 102.29;   delta_RTT= -53.94;      cur_dl_rate= 30000.00;  cur_ul_rate= 25000.00
rx_load= 20159.26;      tx_load= 278.05;        avg_RTT= 54.53; prev_avg_RTT= 97.51;    delta_RTT= -47.75;      cur_dl_rate= 35000.00;  cur_ul_rate= 25000.00
rx_load= 20805.94;      tx_load= 270.49;        avg_RTT= 56.41; prev_avg_RTT= 93.40;    delta_RTT= -41.10;      cur_dl_rate= 40000.00;  cur_ul_rate= 25000.00
rx_load= 24882.18;      tx_load= 322.57;        avg_RTT= 47.75; prev_avg_RTT= 88.84;    delta_RTT= -45.65;      cur_dl_rate= 45000.00;  cur_ul_rate= 25000.00
rx_load= 29497.06;      tx_load= 379.82;        avg_RTT= 67.74; prev_avg_RTT= 86.73;    delta_RTT= -21.10;      cur_dl_rate= 50000.00;  cur_ul_rate= 25000.00
rx_load= 32893.95;      tx_load= 479.43;        avg_RTT= 97.83; prev_avg_RTT= 87.84;    delta_RTT= 11.10;       cur_dl_rate= 55000.00;  cur_ul_rate= 25000.00
rx_load= 24556.36;      tx_load= 311.23;        avg_RTT= 156.49;        prev_avg_RTT= 94.70;    delta_RTT= 68.66;       cur_dl_rate= 45000.00;  cur_ul_rate= 25000.00
rx_load= 40799.29;      tx_load= 545.31;        avg_RTT= 227.19;        prev_avg_RTT= 107.95;   delta_RTT= 132.49;      cur_dl_rate= 35000.00;  cur_ul_rate= 25000.00
rx_load= 29212.39;      tx_load= 753.69;        avg_RTT= 122.95;        prev_avg_RTT= 109.45;   delta_RTT= 15.00;       cur_dl_rate= 40000.00;  cur_ul_rate= 25000.00
rx_load= 28186.01;      tx_load= 447.29;        avg_RTT= 52.41; prev_avg_RTT= 103.75;   delta_RTT= -57.04;      cur_dl_rate= 45000.00;  cur_ul_rate= 25000.00
rx_load= 31596.55;      tx_load= 410.16;        avg_RTT= 71.77; prev_avg_RTT= 100.55;   delta_RTT= -31.97;      cur_dl_rate= 50000.00;  cur_ul_rate= 25000.00
rx_load= 26837.54;      tx_load= 346.42;        avg_RTT= 80.90; prev_avg_RTT= 98.58;    delta_RTT= -19.65;      cur_dl_rate= 45000.00;  cur_ul_rate= 25000.00
rx_load= 33048.64;      tx_load= 423.61;        avg_RTT= 64.38; prev_avg_RTT= 95.16;    delta_RTT= -34.20;      cur_dl_rate= 50000.00;  cur_ul_rate= 25000.00
rx_load= 33315.32;      tx_load= 412.10;        avg_RTT= 93.07; prev_avg_RTT= 94.95;    delta_RTT= -2.09;       cur_dl_rate= 55000.00;  cur_ul_rate= 25000.00
rx_load= 32493.94;      tx_load= 413.36;        avg_RTT= 128.56;        prev_avg_RTT= 98.31;    delta_RTT= 33.60;       cur_dl_rate= 45000.00;  cur_ul_rate= 25000.00
rx_load= 21602.24;      tx_load= 293.82;        avg_RTT= 190.73;        prev_avg_RTT= 107.56;   delta_RTT= 92.42;       cur_dl_rate= 35000.00;  cur_ul_rate= 25000.00
rx_load= 29190.96;      tx_load= 503.91;        avg_RTT= 265.29;        prev_avg_RTT= 123.33;   delta_RTT= 157.73;      cur_dl_rate= 25000.00;  cur_ul_rate= 25000.00
rx_load= 14844.82;      tx_load= 463.83;        avg_RTT= 48.37; prev_avg_RTT= 115.83;   delta_RTT= -74.96;      cur_dl_rate= 20000.00;  cur_ul_rate= 25000.00
rx_load= 12920.46;      tx_load= 250.95;        avg_RTT= 49.89; prev_avg_RTT= 109.24;   delta_RTT= -65.95;      cur_dl_rate= 25000.00;  cur_ul_rate= 25000.00
rx_load= 32228.05;      tx_load= 451.65;        avg_RTT= 47.49; prev_avg_RTT= 103.06;   delta_RTT= -61.75;      cur_dl_rate= 30000.00;  cur_ul_rate= 25000.00
#!/bin/sh

max_ul_rate=35000
min_ul_rate=25000

max_dl_rate=60000
min_dl_rate=20000

tick_duration=2

alpha=0.1

ratio_adjust_down=0.25
ratio_adjust_up=0.125

max_delta_RTT=20

rx_bytes_path="/sys/class/net/wan/statistics/rx_bytes"
tx_bytes_path="/sys/class/net/wan/statistics/tx_bytes"

read -d '' reflectors << EOF
1.1.1.1
8.8.8.8
EOF

no_reflectors=$(echo "$reflectors" | wc -l)

RTTs=$(mktemp)

function get_avg_RTT {

for reflector in $reflectors;
do
        echo $(ping -i 0.2 -c 10 $reflector | tail -1 | awk '{print $4}' | cut -d '/' -f 2) >> $RTTs&
done
wait
avg_RTT=$(echo $(cat $RTTs) | awk '{ total += $2; count++ } END { print total/count }')
> $RTTs
}

function update_rates {
        get_avg_RTT
        delta_RTT=$(echo "scale=4; $avg_RTT - $prev_avg_RTT" | bc)
        prev_avg_RTT=$(echo "scale=4; (1-$alpha)*$prev_avg_RTT+$alpha*$avg_RTT" | bc)

        if [ $(echo "$delta_RTT > $max_delta_RTT" | bc -l) -eq 1 ]; then

                cur_dl_rate=$(echo "scale=4; $cur_dl_rate-$ratio_adjust_down*($max_dl_rate-$min_dl_rate)" | bc)
                cur_ul_rate=$(echo "scale=4; $cur_ul_rate-($max_ul_rate-$min_ul_rate)/4" | bc)
        fi
        cur_rx_bytes=$(cat $rx_bytes_path)
        cur_tx_bytes=$(cat $tx_bytes_path)
        t_cur_bytes=$(date +%s)

        rx_load=$(echo "scale=4; (8/1000)*(($cur_rx_bytes-$prev_rx_bytes)/($t_cur_bytes-$t_prev_bytes))"|bc)
        tx_load=$(echo "scale=4; (8/1000)*(($cur_tx_bytes-$prev_tx_bytes)/($t_cur_bytes-$t_prev_bytes))"|bc)

        t_prev_bytes=$t_cur_bytes
        prev_rx_bytes=$cur_rx_bytes
        prev_tx_bytes=$cur_tx_bytes

        if [ $(echo "$delta_RTT < $max_delta_RTT && $rx_load > 0.5*$cur_dl_rate" |bc) -eq 1 ]; then
                cur_dl_rate=$(echo "scale=4; $cur_dl_rate + $ratio_adjust_up*($max_dl_rate-$min_dl_rate)"|bc)
        fi

        if [ $(echo "$delta_RTT < $max_delta_RTT && $tx_load > 0.5*$cur_ul_rate" |bc) -eq 1 ]; then
                cur_ul_rate=$(echo "scale=4; $cur_ul_rate + $ratio_adjust_up*($max_ul_rate-$min_ul_rate)"|bc)
        fi

        if [ $(echo "$delta_RTT < $max_delta_RTT && $rx_load < 0.5*$cur_dl_rate" |bc) -eq 1 ]; then
                cur_dl_rate=$(echo "scale=4; $cur_dl_rate - $ratio_adjust_down*($max_dl_rate-$min_dl_rate)"|bc)
        fi

        if [ $(echo "$delta_RTT < $max_delta_RTT && $tx_load < 0.5*$cur_ul_rate" |bc) -eq 1 ]; then
                cur_ul_rate=$(echo "scale=4; $cur_ul_rate - $ratio_adjust_down*($max_ul_rate-$min_ul_rate)"|bc)
        fi

        if [ $(echo "$cur_dl_rate<$min_dl_rate" | bc) -eq 1 ]; then
                cur_dl_rate=$min_dl_rate;
        fi

        if [ $(echo "$cur_ul_rate<$min_ul_rate" | bc) -eq 1 ]; then
                cur_ul_rate=$min_ul_rate;
        fi

        if [ $(echo "$cur_dl_rate>$max_dl_rate" | bc) -eq 1 ]; then
                cur_dl_rate=$max_dl_rate;
        fi

        if [ $(echo "$cur_ul_rate>$max_ul_rate" | bc) -eq 1 ]; then
                cur_ul_rate=$max_ul_rate;
        fi

        printf "rx_load= %.2f;\ttx_load= %.2f;\tavg_RTT= %.2f;\tprev_avg_RTT= %.2f;\tdelta_RTT= %.2f;\tcur_dl_rate= %.2f;\tcur_ul_rate= %.2f\n" $rx_load $tx_load $avg_RTT $prev_avg_RTT $delta_RTT $cur_dl_rate $cur_ul_rate
}


get_avg_RTT

prev_avg_RTT=$avg_RTT;

cur_dl_rate=$min_dl_rate
cur_ul_rate=$min_ul_rate

t_prev_bytes=$(date +%s)

prev_rx_bytes=$(cat $rx_bytes_path)
prev_tx_bytes=$(cat $tx_bytes_path)

while true
do
        t_start=$(date +%s)
        update_rates
        tc qdisc change root dev wan cake bandwidth "$cur_ul_rate"Kbit
        tc qdisc change root dev veth-lan cake bandwidth "$cur_dl_rate"Kbit
        t_end=$(date +%s)
#       sleep $(echo "$tick_duration-($t_start-$t_end)"|bc)
done

At the moment I print out with:

 printf "rx_load= %.2f;\ttx_load= %.2f;\tavg_RTT= %.2f;\tprev_avg_RTT= %.2f;\tdelta_RTT= %.2f;\tcur_dl_rate= %.2f;\tcur_ul_rate= %.2f\n" $rx_load $tx_load $avg_RTT $prev_avg_RTT $delta_RTT $cur_dl_rate $cur_ul_rate

Is there a better way? The tabs get screwed up it seems with my present line.

Yes, I would make the 50% a parameter that can be easily changed/set, but I would always base it on the current capacity, because that will keep the meaning of the "load" in percentage easy to understand without additional knowledge :wink:

Probably, but then it will also stay elevated for longer if you encounter sustained overload, without much thinking maybe alpha could be transiently elevated just after a rate decrease to capture the new RTT quicker (under the theory that decreasing the rate should also decrease the measured RTT). But maybe it is better to keep it simpler and simply let the increased RTTs age out...

Here is with alpha set to 0.05. With a constant download the prev_avg_RTT still just explodes. Is this solvable by playing around with different alpha values or does the routine need some alteration I wonder? I am trying to keep everything as simple as possible. And avoid hacks where possible!

root@OpenWrt:~# ./sqm-autorate
     rx_load         tx_load    prev_avg_RTT         avg_RTT       delta_RTT     cur_dl_rate     cur_ul_rate
     1087.94          149.60           48.81           50.77            2.06        20000.00        25000.00
    17229.82         1421.41           48.82           48.91            0.09        25000.00        25000.00
    19685.63         1770.17           48.81           48.68           -0.14        30000.00        25000.00
    18280.51         1944.94           50.18           76.22           27.41        20000.00        25000.00
      390.59         4251.31           50.03           47.14           -3.04        20000.00        25000.00
      674.53         2187.44           49.95           48.34           -1.69        20000.00        25000.00
       27.47           32.88           49.88           48.67           -1.27        20000.00        25000.00
     2272.59          120.40           50.09           54.06            4.18        20000.00        25000.00
    14346.93         1350.61           50.08           49.93           -0.16        25000.00        25000.00
    21321.62         1946.48           50.09           50.22            0.14        30000.00        25000.00
    24148.91         2239.22           50.16           51.48            1.40        35000.00        25000.00
    26466.51         2467.20           50.14           49.74           -0.42        40000.00        25000.00
    34732.43         3221.25           50.20           51.43            1.30        45000.00        25000.00
    35395.06         3256.28           50.38           53.74            3.54        50000.00        25000.00
    38732.93         3661.48           51.25           67.74           17.36        55000.00        25000.00
    47053.11         4500.55           51.40           54.22            2.97        60000.00        25000.00
    46479.71         4526.98           53.86          100.71           49.31        50000.00        25000.00
    42324.76         3964.89           53.93           55.29            1.43        55000.00        25000.00
    45395.13         4278.26           54.58           66.83           12.90        60000.00        25000.00
    45719.27         4459.80           56.12           85.33           30.75        50000.00        25000.00
    43514.26         4195.73           56.98           73.33           17.21        55000.00        25000.00
    44686.23         4206.87           58.34           84.30           27.32        45000.00        25000.00
    38877.59         3659.90           57.98           51.17           -7.17        50000.00        25000.00
    41154.27         4139.25           58.52           68.78           10.80        55000.00        25000.00
    39813.14         3887.48           59.97           87.42           28.90        45000.00        25000.00
    37899.03         3581.61           59.54           51.31           -8.66        50000.00        25000.00
    41976.27         3950.81           59.63           61.45            1.92        55000.00        25000.00
    42499.17         4116.22           60.76           82.12           22.49        45000.00        25000.00
    37726.46         3484.18           60.31           51.94           -8.82        50000.00        25000.00
    41446.68         3805.52           60.08           55.71           -4.61        55000.00        25000.00
    45831.69         4335.87           61.55           89.35           29.27        45000.00        25000.00
    35482.79         3351.14           61.20           54.56           -6.99        50000.00        25000.00
    41890.25         3922.70           60.76           52.50           -8.70        55000.00        25000.00
    43783.31         4183.56           62.61           97.76           37.00        45000.00        25000.00
    39500.22         3644.13           62.04           51.11          -11.50        50000.00        25000.00
    39858.19         3725.80           61.61           53.51           -8.53        55000.00        25000.00
    44659.81         3922.71           61.56           60.49           -1.12        60000.00        25000.00
    48483.09         4773.68           62.91           88.64           27.09        50000.00        25000.00
    44695.51         4294.28           62.76           60.00           -2.91        55000.00        25000.00
    45985.19         4381.92           62.70           61.46           -1.30        60000.00        25000.00
    44926.52         4443.20           65.00          108.74           46.04        50000.00        25000.00
    38267.01         3694.86           64.87           62.31           -2.69        55000.00        25000.00
    47581.88         4079.12           64.64           60.37           -4.49        60000.00        25000.00
    46362.82         4467.83           65.62           84.26           19.62        60000.00        25000.00
    44945.89         4155.15           67.54          104.06           38.44        50000.00        25000.00
    43910.34         3473.22           68.36           83.93           16.39        55000.00        25000.00
    44077.75         2644.01           70.37          108.50           40.13        45000.00        25000.00

As you can see I fixed the formatting issues :smiley:.

Originally I had wondered about only updating prev_avg_RTT when load is less than half the minimum set (i.e. more or less unloaded). But that would be a significant departure from the routine you proposed.

Generally speaking I think the routine seems very promising.

How about using two different alphas depending whether the current RTT value would in- or decrease the long-term average? That should allow faster down than upscaling, to keep the bufferbloat detection snappy?

The rationale for slow increase is simply, that bufferbloat increases the RTT in relative short timescales, while persistent RTT changes (e.g. due to routing changes) will be relative rare and far between. And the "cost" of underestimating the real path RTT will just result in a bit sacrificied throughput, which at least will not increase bufferbloat.
The rationale for fast decrease is that ideally the average RTT should be close to the minimal RTT over a given path, as we try to control the most likely cause for large RTTs, and erring on setting the avgRTT too low has acceptable bufferbloat consequences.

So maybe that is not that hackish after all :wink:

P.S.: Could your present the rx/tx load as ratios of the currently set shaper rate, currentkly I am at a loss what these numbers mean...

I like it! Please can you suggest initial values for me to try?

They are just the present rx and tx load in Kbit/s, which matches the units of the current download and upload rates and those that I set in tc qdisc change. Does that make sense? I can still change if you think I should. So that would mean making the loads a fraction of the presently set download rate / upload rate? And then rather than compare load in Kbit/s with load_thresh*cur_dl/ul_rate I would instead just compare rx_load with load_thresh. What do you think is best?

By the way do the throughput values on the left even just very roughly correspond with the CAKE bandwidths set on the right? They certainly scale together, but there is a big loss. I have set the overhead to 92 based on the above. Perhaps that accounts for the big difference.

Ah okay, typically I am a friend of raw values like these, but I think that a percentage threshold will scale better to human psychology (where we minimal noticeable change tends to depend on the current level), it certainly will be easier to read :wink:

Oh, this is your baby now, you make the decisions; I am just sitting in the peanut gallery :wink:

That, I believe will be easier to think about.

Well if you increase the shaper rate once the load hits 50%, you can only expect 100% load once you reached the maximum, no? I would not expect these to be precisely matched as they might differ in how much overhead they include (but that is not your issue here, on the wan interface wireguard overhead is part of the payload). Maybe you could extract the load counters also directly from cake (but that might be costly)?

Am I right that you meant something like this:

        if [ $(echo "$delta_RTT>=0" | bc) -eq 1 ]; then
                prev_avg_RTT=$(echo "scale=4; (1-$alpha_RTT_increase)*$prev_avg_RTT+$alpha_RTT_increase*$avg_RTT" | bc)
        else
                prev_avg_RTT=$(echo "scale=4; (1-$alpha_RTT_decrease)*$prev_avg_RTT+$alpha_RTT_decrease*$avg_RTT" | bc)
        fi

With alpha_RTT_increase=0.025 and alpha_RTT_decrease=0.1 this gives:

root@OpenWrt:~# ./sqm-autorate
     rx_load         tx_load    prev_avg_RTT         avg_RTT       delta_RTT     cur_dl_rate     cur_ul_rate
        0.00            0.00           48.97           49.21            0.25        20000.00        25000.00
        0.27            0.00           49.00           50.30            1.33        20000.00        25000.00
        0.70            0.01           49.16           55.09            6.08        25000.00        25000.00
        0.75            0.01           49.06           48.20           -0.95        30000.00        25000.00
        0.54            0.01           49.12           51.59            2.53        35000.00        25000.00
        0.75            0.02           49.43           61.42           12.30        40000.00        25000.00
        1.05            0.02           49.93           69.50           20.07        30000.00        25000.00
        0.75            0.02           50.10           56.53            6.59        35000.00        25000.00
        1.04            0.02           50.66           72.72           22.62        25000.00        25000.00
        0.64            0.01           50.61           50.15           -0.51        30000.00        25000.00
        0.60            0.01           50.71           54.76            4.14        35000.00        25000.00
        0.77            0.02           50.87           56.95            6.24        40000.00        25000.00
        0.99            0.02           51.94           93.46           42.59        30000.00        25000.00
        0.70            0.02           51.92           51.80           -0.13        35000.00        25000.00
        0.49            0.01           51.45           47.24           -4.68        25000.00        25000.00
        1.13            0.01           52.01           73.70           22.25        20000.00        25000.00
        0.66            0.01           51.97           51.61           -0.40        25000.00        25000.00
        0.52            0.01           51.76           49.86           -2.11        30000.00        25000.00
        0.75            0.02           51.86           55.95            4.19        35000.00        25000.00
        0.71            0.01           51.59           49.17           -2.70        40000.00        25000.00
        0.74            0.02           51.63           53.02            1.43        45000.00        25000.00
        0.83            0.02           51.92           63.26           11.63        50000.00        25000.00
        0.83            0.03           52.27           65.83           13.90        55000.00        25000.00
        0.97            0.03           53.47          100.52           48.25        45000.00        25000.00
        0.83            0.03           53.59           58.26            4.79        50000.00        25000.00
        0.44            0.01           53.97           68.72           15.13        40000.00        25000.00
        0.64            0.02           53.70           51.22           -2.75        45000.00        25000.00
        0.68            0.02           53.80           57.84            4.15        50000.00        25000.00
        0.86            0.02           56.71          170.22          116.42        40000.00        25000.00
        0.75            0.02           56.88           63.41            6.70        45000.00        25000.00
        0.77            0.02           57.37           76.56           19.68        50000.00        25000.00
        1.02            0.02           61.91          238.84          181.47        40000.00        25000.00
        0.61            0.02           60.86           51.45          -10.46        45000.00        25000.00
        0.61            0.02           60.85           60.74           -0.12        50000.00        25000.00
        0.96            0.02           66.25          277.11          216.26        40000.00        25000.00
        0.59            0.02           69.76          206.57          140.32        30000.00        25000.00
        0.70            0.02           68.06           52.74          -17.02        35000.00        25000.00
        0.73            0.02           66.57           53.20          -14.86        40000.00        25000.00
        0.57            0.01           64.89           49.77          -16.80        45000.00        25000.00
        0.73            0.02           63.80           53.98          -10.92        50000.00        25000.00

Does that mean I need to be more brutal with my alpha differential? What would you suggest for these?

Full code:

#!/bin/sh

max_ul_rate=35000
min_ul_rate=25000

max_dl_rate=60000
min_dl_rate=20000

tick_duration=3

alpha_RTT_increase=0.025
alpha_RTT_decrease=0.1

ratio_adjust_down=0.25
ratio_adjust_up=0.125

load_thresh=0.5

max_delta_RTT=20

rx_bytes_path="/sys/class/net/wan/statistics/rx_bytes"
tx_bytes_path="/sys/class/net/wan/statistics/tx_bytes"

read -d '' reflectors << EOF
1.1.1.1
8.8.8.8
EOF

no_reflectors=$(echo "$reflectors" | wc -l)

RTTs=$(mktemp)

function get_avg_RTT {

for reflector in $reflectors;
do
        echo $(ping -i 0.2 -c 10 $reflector | tail -1 | awk '{print $4}' | cut -d '/' -f 2) >> $RTTs&
done
wait
avg_RTT=$(echo $(cat $RTTs) | awk '{ total += $2; count++ } END { print total/count }')
> $RTTs
}

function update_rates {
        get_avg_RTT
        delta_RTT=$(echo "scale=4; $avg_RTT - $prev_avg_RTT" | bc)

        if [ $(echo "$delta_RTT>=0" | bc) -eq 1 ]; then
                prev_avg_RTT=$(echo "scale=4; (1-$alpha_RTT_increase)*$prev_avg_RTT+$alpha_RTT_increase*$avg_RTT" | bc)
        else
                prev_avg_RTT=$(echo "scale=4; (1-$alpha_RTT_decrease)*$prev_avg_RTT+$alpha_RTT_decrease*$avg_RTT" | bc)
        fi

        if [ $(echo "$delta_RTT > $max_delta_RTT" | bc -l) -eq 1 ]; then

                cur_dl_rate=$(echo "scale=4; $cur_dl_rate-$ratio_adjust_down*($max_dl_rate-$min_dl_rate)" | bc)
                cur_ul_rate=$(echo "scale=4; $cur_ul_rate-($max_ul_rate-$min_ul_rate)/4" | bc)
        fi
        cur_rx_bytes=$(cat $rx_bytes_path)
        cur_tx_bytes=$(cat $tx_bytes_path)
        t_cur_bytes=$(date +%s)

        rx_load=$(echo "scale=10; (8/1000)*(($cur_rx_bytes-$prev_rx_bytes)/($t_cur_bytes-$t_prev_bytes)*(1/$cur_dl_rate))"|bc)
        tx_load=$(echo "scale=10; (8/1000)*(($cur_tx_bytes-$prev_tx_bytes)/($t_cur_bytes-$t_prev_bytes)*(1/$cur_ul_rate))"|bc)

        t_prev_bytes=$t_cur_bytes
        prev_rx_bytes=$cur_rx_bytes
        prev_tx_bytes=$cur_tx_bytes

        if [ $(echo "$delta_RTT < $max_delta_RTT && $rx_load > $load_thresh" |bc) -eq 1 ]; then
                cur_dl_rate=$(echo "scale=4; $cur_dl_rate + $ratio_adjust_up*($max_dl_rate-$min_dl_rate)"|bc)
        fi

        if [ $(echo "$delta_RTT < $max_delta_RTT && $tx_load > $load_thresh" |bc) -eq 1 ]; then
                cur_ul_rate=$(echo "scale=4; $cur_ul_rate + $ratio_adjust_up*($max_ul_rate-$min_ul_rate)"|bc)
        fi

        if [ $(echo "$delta_RTT < $max_delta_RTT && $rx_load < $load_thresh" |bc) -eq 1 ]; then
                cur_dl_rate=$(echo "scale=4; $cur_dl_rate - $ratio_adjust_down*($max_dl_rate-$min_dl_rate)"|bc)
        fi

        if [ $(echo "$delta_RTT < $max_delta_RTT && $tx_load < $load_thresh" |bc) -eq 1 ]; then
                cur_ul_rate=$(echo "scale=4; $cur_ul_rate - $ratio_adjust_down*($max_ul_rate-$min_ul_rate)"|bc)
        fi

        if [ $(echo "$cur_dl_rate<$min_dl_rate" | bc) -eq 1 ]; then
                cur_dl_rate=$min_dl_rate;
        fi

        if [ $(echo "$cur_ul_rate<$min_ul_rate" | bc) -eq 1 ]; then
                cur_ul_rate=$min_ul_rate;
        fi

        if [ $(echo "$cur_dl_rate>$max_dl_rate" | bc) -eq 1 ]; then
                cur_dl_rate=$max_dl_rate;
        fi

        if [ $(echo "$cur_ul_rate>$max_ul_rate" | bc) -eq 1 ]; then
                cur_ul_rate=$max_ul_rate;
        fi

        printf "%12.2f\t%12.2f\t%12.2f\t%12.2f\t%12.2f\t%12.2f\t%12.2f\n" $rx_load $tx_load $prev_avg_RTT $avg_RTT $delta_RTT $cur_dl_rate $cur_ul_rate
}


get_avg_RTT

prev_avg_RTT=$avg_RTT;

cur_dl_rate=$min_dl_rate
cur_ul_rate=$min_ul_rate

t_prev_bytes=$(date +%s)

prev_rx_bytes=$(cat $rx_bytes_path)
prev_tx_bytes=$(cat $tx_bytes_path)

printf "%12s\t%12s\t%12s\t%12s\t%12s\t%12s\t%12s\t%12s\n" "rx_load" "tx_load" "prev_avg_RTT" "avg_RTT" "delta_RTT" "cur_dl_rate" "cur_ul_rate"

while true
do
        t_start=$(date +%s)
        update_rates
        tc qdisc change root dev wan cake bandwidth "$cur_ul_rate"Kbit
        tc qdisc change root dev veth-lan cake bandwidth "$cur_dl_rate"Kbit
        t_end=$(date +%s)
        sleep $(echo "$tick_duration-($t_start-$t_end)"|bc)
done
1 Like

No idea, mybe it is time to actually test this in the real world and then change the alphas and see whether it feels better or worse? (Not the most scientific approach, but after all your subjective assessment is the final arbiter anyway)

1 Like

OK will do. From initial testing it seems like a challenge is to stop the average _RTT getting too large (e.g. > 100ms) following each ascent in the sawtooth to the capacity limit and eventual overshoot (before the bandwidth is corrected back down).

Any clue at what average_RTT you would start to see glitches in zoom / teams calls? I think it is higher than I previously thought. The waveform bufferbloat website allows an increase of average latency of up to 30ms before downgrading from A to B I think:

Less than 5 ms latency increase - A+
Less than 30 ms latency increase - A
Less than 60 ms latency increase - B
Less than 200 ms latency increase - C
Less than 400 ms latency increase - D
400 ms or greater latency increase - F

It is tempting to set an upper limit 'capacity estimate' upon a downward inflection (once bufferbloat detected) + timeout for that capacity estimate(?), but this adds more complexity and I am trying to avoid arbitrary hacks, favouring a simple algorithm.

Will see what I can manage with tweaking the parameters. I am thinking small tick duration (like 2 seconds or smaller) and small bandwidth increases will help.

In the meantime, quick question: do I need to be concerned about seeing rx_load > 1.0? It is calculated based on the cur_dl_rate from the previous tick. Since I thought 'tc qdisc change' gives an instant bandwidth change I am slightly bemused why it sometimes goes above 1.0.

And with:

echo $(ping -i 0.2 -c 10 $reflector | tail -1 | awk '{print $4}' | cut -d '/' -f 2) >> $RTTs&

Any thoughts about the '0.2'? Could that be safely decreased further? I am trying to see how fast I can make the ticks. Could it be '0' in fact? How many simultaneous instantaneous pings can a connection manage I wonder.

I guess that statistics keeping is generally considered to be okay if not fully temporally precise... I would probably try to get the transfer estimates from tc -s qdisc instead of getting the wan stats, by I have no idea how temporally precise these are in comparison.

I think traditionally you need to be root to do so, not an issue under OpenWrt, but then on slow links that might already cause a noticeable load in itself.... I would, probably naively, try to see wether I can get away with a cycle time of 10-30 seconds and then getting by with the normal ping interval should work.

Nah, you do not want to have your ICMP probes cause congestion themselves :wink:

1 Like

Sounds like you guys are building a fine PID controller. Do you think it will work well on my electric BBQ Smoker? :rofl:

Hmmm, wonder how well a PID approach to the setting of the levels controls might work....

(don't mind me... I've got myself about half convinced I'm following what you guys are doing there)

2 Likes

Mmmh, wouldn't we need to scale the size of the rate change to the amount of measured deltaRTT then? I am not firm on control theory (I work on wet ware for a living)... but I guess this is great advice to look into the literature instead of cobbling something together on the fly.
Then again, only talking for myself, so @Lynx and @dlakelan might disagree, this learning by doing trial and error approach is fun (especially since I only play the role of the armchair designer, having borrowed the inspiration from @dlakelan 's earlier solution, and letting @Lynx deal with implementation and testing).

1 Like

I still think Bayesian inference for the current BW ceiling would be ideal to converge quickly on the right value rather than oscillating or overly restricting your BW.

Suppose you observe BW1 at time 1 and no significant bufferbloat, and at time 2 you observe BW2 and bloat. You have a prior over the maximum rate of change the available bandwidth can undergo, you have a cost per second of bufferbloat, and a cost per second of lost bandwidth opportunity. You place a distribution over the likely rates based on maximum rate of change, you truncate it to the current set point if you see bufferbloat (since it must be below the current value in that case) then you choose a set point based on optimizing the expected cost. You might get away with a truncated gaussian as the distribution and a relatively simple optimization. Don't try this in shell script though!

1 Like

@moeller0 is that wetware as in an anorak or wetware as in biological processing I wonder? I totally don't mind trying out different ideas. Very much enjoying it! Without wanting to sound too creepy, in trying to better understand elements of CAKE I have read your posts on this forum on SQM for months and you appear to have a handle on this that very few people do. I originally tried CAKE on Asus Merlin and your posts on this forum and the SQM implementation code promoted me to try OpenWrt in the first place with my RT3200. Really glad I did. In any case, for me it's an honour that you take the time to reply to my messages on this.

And to both @moeller0 and @dlakelan I really appreciate and welcome the suggestions. I have not had time yet to try to optimise the bash script I have, but yes I also have the nagging feeling that the approach might be improved on a theoretical level.

In my experience with solving problems like this and coding in general it can be helpful to write a bit then try then write a bit more then try further. So iterative approach and back to drawing board sometimes is familiar territory for me.

Perhaps the oscillation doesn't make so much sense because although the bandwidth changes it doesn't change that frequently I don't think. So perhaps it doesn't need to keep testing the ceiling. I think we may need some form of slow moving capacity estimation. Right at the begining of this thread I referenced the approach scripted by @Bndwdthseekr and @richb-hanover-priv here:

There is something neat about coming into action every now and then to estimate capacity and then going to sleep. But I don't like saturating the line to do that because it means causing bufferbloat every time it comes into action. So I think this approach is broken for that reason.

I think we need some way of tracking capacity.

@dlakelan could you elaborate how your idea would avoid oscillation?

On the other hand I think the basic gist of the script works. It could well be that tweaking it I'd get something that's acceptable. But I think it's worth exploring how it might be improved on theoretical level. That said, I still strongly favour simplicity and @moeller0's present approach has that in its favour.

In essence I think you're right, and in some sense the "slow moving" would be built in to a Bayesian model. Suppose you saturate your line for a while, and you somehow converge to set the bandwidth right below the real available amount. Now your speed test ends... In the 1 second after that speed test, how much do you believe the available bandwidth could move? Not much right? So the Bayesian posterior is tightly concentrated around the last known good value. But as time goes on, the bandwidth could change... your knowledge of what it is as represented by a probability distribution spreads out... After a long time, your probability distribution would be something like a uniform(0,maxcontracted) distribution.

If it's over-night for example, and no-one is using your connection at all, then there's no information we have about what it is. But every time some transfer occurs and no bloat occurs... we suddenly can put a lower bound on what the available values are at that time. And when we have a pulse of packets that induces bufferbloat we can put an upper bound on the available amount. Since the pulse of packets rarely is just all in one shot, you'll have recent bandwidth values to set a lower bound once you start to get bufferbloat, so you'll have some kind of good estimate of an interval in which your available bandwidth lies.

By using that information and then doing a kind of "optimization" we'll be able to pick a bandwidth value that doesn't waste too much bandwidth, but doesn't allow bufferbloat either. Once we've got that estimate, then as time goes on, the credible values will start spreading out again. The idea I think is that you're always tracking an interval in which the real bandwidth probably lies, and when you get bloat, you could somehow immediately calculate the "best" value to reduce your bandwidth to. And, when your current usage gets near to the lower bound... you'd have a "best value" to increase to as well.

1 Like