Imagine we have a network stack that schedules traffic sends at 4 priority levels [0,3]:
- Generally, two messages at priority 0 are sent for every one message sent at priority 1.
- Likewise, two messages at priority 1 are sent for every one message sent at priority 2, and so on.
- Additionally, higher priority messages scheduled earlier are never usurped by lower priority messages scheduled after.
- But higher priority messages scheduled after may usurp lower priority messages scheduled before.
RakNet has such an algorithm which uses a MIN heap with weights determined by the algorithm below. I would like to better understand the math behind this algorithm.
Simplified/cleaned from RakNet
int NUMBER_OF_PRIORITIES = 4;
int heapWeights[NUMBER_OF_PRIORITIES];
Heap<int, InternalPacket*, false> packetQueue;
void InitHeapWeights(void)
{
for (int priorityLevel = 0; priorityLevel < NUMBER_OF_PRIORITIES; priorityLevel++)
heapWeights[priorityLevel] = (1 << priorityLevel) * priorityLevel + priorityLevel;
}
int GetNextWeight(int priorityLevel)
{
int next = heapWeights[priorityLevel];
if (packetQueue.Size() > 0)
{
int peekPriorityLevel = packetQueue.Peek()->priority;
int weight = packetQueue.PeekWeight();
int min = weight - (1 << peekPriorityLevel) * peekPriorityLevel + peekPriorityLevel;
if (next < min)
next = min + (1 << priorityLevel) * priorityLevel + priorityLevel;
heapWeights[priorityLevel] = next + (1 << priorityLevel) * (priorityLevel + 1) + priorityLevel;
}
else
{
InitHeapWeights();
}
return next;
}
What I Understand So Far
The algorithm always returns whatever state is in the seed array(int heapWeights[NUMBER_OF_PRIORITIES];
), and the value it computes is for the next time the algorithm is called.
State is kept in two places:
- The seed array, whose values scale upwards as more messages are scheduled at a given priority
- The
packetQueue
(min heap): the algorithm uses the priority level of the next-in-line message to compute some sort of minimum weight for the next message at that priority level.
For 4 priority levels, void InitHeapWeights(void)
initializes the seed array to [0, 3, 10, 27] and resets when the queue is empty (but not before returning the latest value for a given priority level)
The initial weights (w0) are calculated as: w0 = 2^priorityLevel * priorityLevel + priorityLevel;
Generally, when the weight is updated, we do: w1 = w0 + 2^priorityLevel * (priorityLevel + 1) + priorityLevel
The edge case scenario checks whether w0 is less than the weight of the next-in-line message minus w0 for the next-in-line's priority level. I'm not sure why that is.