Recap: I’ve defined ‘modem loading factor’ as the upstream speed limiter IP PDU rate set by the Firebrick expressed as a fraction of the theoretical maximum possible IP PDU rate when protocol overheads are removed from the sync rate, and I am taking the latter to be 0.884434 * sync rate. It turns out that a modem loading factor of 100% is not a good idea, and possibly not even workable, as the theoretical maximum is calculated solely on the basis of protocol overheads, total bytes-bloat, and there may be other limiting factors that I am unaware of. I’m using 97%, based on experiment, as a practical maximum, because any higher and the latency figures go through the roof, and I’m taking this to be evidence of ingress queuing at the modem. It is undesirable to have very high latency figures anyway so there is no way I would want to get those last few percentage points of throughout even if there actually is extra true throughout to be had from pushing it harder and harder towards 100%.

I’ve now successfully completed a program to assign modem loading factors to all links dynamically based on the relative sync rates. If one modem is substantially slower than the others, that link is assigned an MLF of 70%, a number chosen by experiment and 95% fir all the others. If the sync rates are all the same then it’s MLF=95% for all. If one modem is only a little slower than the others then the MLF is set dynamically according to the size of the sync rate gap, where the sync rate gap is defined as the distance between the normalised speed of the slowest link and the normalised value of the average of the speeds of the rest of the links, where all sync rates are pre-normalised, rescaled, such that 1.0=the fastest line’s sync rate.

I understand so little about what is going on here and I just don’t know why the particular set of parameters that has been discovered experimentally is the right one. The optimal recipe discovered amounts to making it go slower [!] by rate limiter enforcement at a lower figure and yet this has the effect of making the overall thing faster in total effective throughout terms. What the blinky, blonky blimey.

So the code will now do the right thing if (i) a different line becomes the slowest one, or (ii) the lines have the same speed. Case (iii) the lines have nearly the same speeds, so the slowest one is only slightly slower than the rest, has not been tested and it is not known what the right strategy is. In case (iii), the closer the minimum speed line’s sync rate is to that of the others, the nearer the current implementation sets the MLF of the slowest line, and the value goes up until it approaches the MLF value used for all the other lines.

The chosen function that maps gap size to MLF is as follows: it has a linear region, outside which there is a clamp on output maximum and minimum MLF values at MLF=0.70 min and MLF=0.97 maximum. So it looks something like

y = MLF = f(x)

where

MLF = max( clamp_min, min( clamp_max, m * x + c ) )

where x is the normalised sync rate gap size and clamp_min, clamp_max, m and c are constants.

others = { sync_rates[]

**∖** arg min( sync_rates[] ) }

gap = avg( others[] ) - min( sync_rates[] )

gap_normalised = gap / max( sync_rates[] )

It turned out to be more convenient to calculate others using

avg( others ) = ( sum( sync_rates ) - min( sync_rates) ) / ( n( sync_rates ) - 1 )