Skip to content

Commit 2c5b107

Browse files
committed
Added the HPFQ scheduler and reference scheduling
This commit adds the HierarchicalPacket Fair Queueing (HPFQ) scheduler from the paper "Programmable packet scheduling at line rate" by Sivaraman, Anirudh, et al. This commit also changes the framework to make it easy to schedule references to flows, queues, or schedulers. It does so by adding a reference with every enqueue operation. By adding the reference with the packet or flow, we can create a rank from packet or flow and queue the reference. This commit adds the HierarchicalPacket Fair Queueing (HPFQ) scheduler from the paper "Programmable packet scheduling at line rate" by Sivaraman, Anirudh, et al. This commit also changes the framework to make it easy to schedule references to flows, queues, or schedulres. Signed-off-by: Frey Alfredsson <[email protected]>
1 parent 145b33d commit 2c5b107

File tree

6 files changed

+125
-31
lines changed

6 files changed

+125
-31
lines changed

queue-exp/pifo_fifo.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,13 @@
3939
class Fifo(SchedulingAlgorithm):
4040
"""First in, first out (FIFO)"""
4141

42-
def __init__(self):
42+
def __init__(self, name=None):
43+
super().__init__(name)
4344
self._pifo = Pifo()
4445

45-
def enqueue(self, item):
46+
def enqueue(self, ref, item):
4647
rank = self.get_rank(item)
47-
self._pifo.enqueue(item, rank)
48+
self._pifo.enqueue(ref, rank)
4849

4950
def get_rank(self, item):
5051
return self._pifo.qlen

queue-exp/pifo_hpfq.py

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
#!/usr/bin/env python3
2+
# coding: utf-8 -*-
3+
#
4+
# SPDX-License-Identifier: GPL-3.0-or-later
5+
#
6+
# pifo-hpfq.py
7+
8+
"""HierarchicalPacket Fair Queueing (HPFQ)
9+
10+
This scheduling algorithm is mentioned in the paper "Programmable packet
11+
scheduling at line rate" by Sivaraman, Anirudh, et al. It creates a hierarchy of
12+
WFQ schedulers. The central scheduler is called root and contains references to
13+
other WFQ schedulers. Those two WFQ schedulers are called left and right. We
14+
chose that packets with flow ids lower than ten go into the left scheduler in
15+
our implementation. In contrast, the others go into the right scheduler."""
16+
17+
__copyright__ = """
18+
Copyright (c) 2021, Toke Høiland-Jørgensen <[email protected]>
19+
Copyright (c) 2021, Frey Alfredsson <[email protected]>
20+
"""
21+
22+
__license__ = """
23+
This program is free software: you can redistribute it and/or modify
24+
it under the terms of the GNU General Public License as published by
25+
the Free Software Foundation, either version 3 of the License, or
26+
(at your option) any later version.
27+
28+
This program is distributed in the hope that it will be useful,
29+
but WITHOUT ANY WARRANTY; without even the implied warranty of
30+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
31+
GNU General Public License for more details.
32+
33+
You should have received a copy of the GNU General Public License
34+
along with this program. If not, see <http://www.gnu.org/licenses/>.
35+
"""
36+
37+
from pifo_lib import Packet, Runner, Pifo
38+
from pifo_lib import SchedulingAlgorithm
39+
from pifo_wfq import Wfq
40+
41+
42+
class Hpfq(SchedulingAlgorithm):
43+
"""HierarchicalPacket Fair Queueing (HPFQ)"""
44+
45+
def __init__(self, name=None):
46+
super().__init__(name)
47+
self._root = Wfq("root")
48+
self._left = Wfq("Left")
49+
self._right = Wfq("Right")
50+
51+
def enqueue(self, ref, pkt):
52+
queue = None
53+
if pkt.flow < 10:
54+
self._left.enqueue(ref, pkt)
55+
queue = self._left
56+
else:
57+
self._right.enqueue(ref, pkt)
58+
queue = self._right
59+
60+
self._root.enqueue(queue, pkt)
61+
62+
def dequeue(self):
63+
queue = self._root.dequeue()
64+
return queue.dequeue() if queue is not None else None
65+
66+
def dump(self):
67+
print(" Root:")
68+
self._root.dump()
69+
print(" Left:")
70+
self._left.dump()
71+
print(" Right:")
72+
self._right.dump()
73+
74+
75+
if __name__ == "__main__":
76+
pkts = [
77+
Packet(flow=1, idn=1, length=200),
78+
Packet(flow=1, idn=2, length=200),
79+
Packet(flow=10, idn=1, length=200),
80+
Packet(flow=10, idn=2, length=200),
81+
Packet(flow=2, idn=1, length=100),
82+
Packet(flow=2, idn=2, length=100),
83+
Packet(flow=2, idn=3, length=100),
84+
Packet(flow=20, idn=1, length=100),
85+
Packet(flow=20, idn=2, length=100),
86+
Packet(flow=20, idn=3, length=100),
87+
]
88+
Runner(pkts, Hpfq()).run()

queue-exp/pifo_lib.py

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ def run(self):
4848
print(" Inserting packets into scheduler:")
4949
pprint(self.input_pkts, indent=4)
5050
for p in self.input_pkts:
51-
self.scheduler.enqueue(p)
51+
self.scheduler.enqueue(p, p)
5252
print(" Scheduler state:")
5353
self.scheduler.dump()
5454
output = []
@@ -70,10 +70,10 @@ class SchedulingAlgorithm():
7070
Please look at the pifo_fifo.py to see how you implement a FIFO.
7171
"""
7272

73-
def __init__(self):
74-
raise NotImplementedError(self.__class__.__name__ + ' missing implementation')
73+
def __init__(self, name=None):
74+
self._name = name
7575

76-
def enqueue(self, item):
76+
def enqueue(self, pkt):
7777
raise NotImplementedError(self.__class__.__name__ + ' missing implementation')
7878

7979
def dequeue(self):
@@ -83,25 +83,28 @@ def dump(self):
8383
raise NotImplementedError(self.__class__.__name__ + ' missing implementation')
8484

8585
def __next__(self):
86-
item = self.dequeue()
87-
if item is None:
86+
pkt = self.dequeue()
87+
if pkt is None:
8888
raise StopIteration
89-
return item
89+
return pkt
9090

9191
def __iter__(self):
9292
return self
9393

9494
def __repr__(self):
95-
return f"{self.__class__.__name__} - {self.__class__.__doc__}"
95+
result = f"{self.__class__.__name__} - {self.__class__.__doc__}"
96+
if self._name is not None:
97+
result = f"{self._name}: {result}"
98+
return result
9699

97100

98101
class Queue:
99102
def __init__(self, idx=None):
100103
self._list = []
101104
self.idx = idx
102105

103-
def enqueue(self, item):
104-
self._list.append(item)
106+
def enqueue(self, ref, rank=None):
107+
self._list.append(ref)
105108

106109
def peek(self):
107110
try:
@@ -139,11 +142,11 @@ def dump(self):
139142

140143

141144
class Pifo(Queue):
142-
def enqueue(self, item, rank):
145+
def enqueue(self, ref, rank):
143146
if rank is None:
144147
raise ValueError("Rank can't be of value 'None'.")
145148

146-
super().enqueue((rank, item))
149+
super().enqueue((rank, ref))
147150
self.sort()
148151

149152
def sort(self):
@@ -160,8 +163,7 @@ def peek(self):
160163

161164
class Flow(Queue):
162165
def __init__(self, idx):
163-
super().__init__()
164-
self.idx = idx
166+
super().__init__(idx)
165167

166168
def __repr__(self):
167169
return f"F(I:{self.idx}, Q:{self.qlen}, L:{self.length})"

queue-exp/pifo_srpt.py

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,8 @@
4444
class Srpt(SchedulingAlgorithm):
4545
"""Shortest Remaining Processing Time"""
4646

47-
def __init__(self):
47+
def __init__(self, name=None):
48+
super().__init__(name)
4849
self._pifo = Pifo()
4950
self._flow_tracker = FlowTracker()
5051

@@ -57,22 +58,22 @@ def __init__(self):
5758
else:
5859
self._remains[pkt.flow] = pkt.length
5960

60-
def get_rank(self, pkt):
61-
rank = self._remains[pkt.flow]
62-
self._remains[pkt.flow] -= pkt.length
61+
def get_rank(self, item):
62+
rank = self._remains[item.flow]
63+
self._remains[item.flow] -= item.length
6364
return rank
6465

65-
def enqueue(self, item):
66+
def enqueue(self, ref, item):
6667
flow = self._flow_tracker.enqueue(item)
6768
rank = self.get_rank(item)
6869
self._pifo.enqueue(flow, rank)
6970

7071
def dequeue(self):
7172
flow = self._pifo.dequeue()
72-
pkt = None
73+
item = None
7374
if flow is not None:
74-
pkt = flow.dequeue()
75-
return pkt
75+
item = flow.dequeue()
76+
return item
7677

7778
def dump(self):
7879
self._pifo.dump()

queue-exp/pifo_stfq.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,8 @@
4141
class Stfq(SchedulingAlgorithm):
4242
"""Start-Time Fair Queuing (STFQ)"""
4343

44-
def __init__(self):
44+
def __init__(self, name=None):
45+
super().__init__(name)
4546
self._pifo = Pifo()
4647

4748
self._last_finish = {}
@@ -56,9 +57,9 @@ def get_rank(self, pkt):
5657
self._last_finish[flow_id] = rank + pkt.length
5758
return rank
5859

59-
def enqueue(self, item):
60+
def enqueue(self, ref, item):
6061
rank = self.get_rank(item)
61-
self._pifo.enqueue(item, rank)
62+
self._pifo.enqueue(ref, rank)
6263

6364
def dequeue(self):
6465
return self._pifo.dequeue()

queue-exp/pifo_wfq.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,8 @@
4141
class Wfq(SchedulingAlgorithm):
4242
"""Weighted Fair Queueing (WFQ)"""
4343

44-
def __init__(self):
44+
def __init__(self, name=None):
45+
super().__init__(name)
4546
self._pifo = Pifo()
4647
self._last_finish = {}
4748
self._virt_time = 0
@@ -56,9 +57,9 @@ def get_rank(self, pkt):
5657
self._last_finish[flow] = rank + pkt.length / weight
5758
return rank
5859

59-
def enqueue(self, item):
60+
def enqueue(self, ref, item):
6061
rank = self.get_rank(item)
61-
self._pifo.enqueue(item, rank)
62+
self._pifo.enqueue(ref, rank)
6263

6364
def dequeue(self):
6465
return self._pifo.dequeue()

0 commit comments

Comments
 (0)