Skip to content

Commit 1d341c1

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 1d341c1

File tree

6 files changed

+149
-49
lines changed

6 files changed

+149
-49
lines changed

queue-exp/pifo_fifo.py

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -32,23 +32,24 @@
3232
along with this program. If not, see <http://www.gnu.org/licenses/>.
3333
"""
3434

35-
from pifo_lib import Packet, Runner, Pifo
36-
from pifo_lib import SchedulingAlgorithm
35+
from sched_lib import Packet, Runner, Pifo, SchedulingAlgorithm
3736

3837

3938
class Fifo(SchedulingAlgorithm):
4039
"""First in, first out (FIFO)"""
4140

42-
def __init__(self):
41+
def __init__(self, name=None):
42+
super().__init__(name)
4343
self._pifo = Pifo()
4444

45-
def enqueue(self, item):
46-
rank = self.get_rank(item)
47-
self._pifo.enqueue(item, rank)
48-
49-
def get_rank(self, item):
45+
def get_rank(self, _):
46+
"""Rank the items in FIFO order."""
5047
return self._pifo.qlen
5148

49+
def enqueue(self, ref, item):
50+
rank = self.get_rank(item)
51+
self._pifo.enqueue(ref, rank)
52+
5253
def dequeue(self):
5354
return self._pifo.dequeue()
5455

queue-exp/pifo_hpfq.py

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
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 sched_lib import Packet, Runner, SchedulingAlgorithm
38+
from pifo_wfq import Wfq
39+
40+
41+
class Hpfq(SchedulingAlgorithm):
42+
"""HierarchicalPacket Fair Queueing (HPFQ)"""
43+
44+
def __init__(self, name=None):
45+
super().__init__(name)
46+
self._root = Wfq("root")
47+
self._left = Wfq("Left")
48+
self._right = Wfq("Right")
49+
50+
def enqueue(self, ref, item):
51+
queue = None
52+
if item.flow < 10:
53+
self._left.enqueue(ref, item)
54+
queue = self._left
55+
else:
56+
self._right.enqueue(ref, item)
57+
queue = self._right
58+
59+
self._root.enqueue(queue, item)
60+
61+
def dequeue(self):
62+
queue = self._root.dequeue()
63+
return queue.dequeue() if queue is not None else None
64+
65+
def dump(self):
66+
print(" Root:")
67+
self._root.dump()
68+
print(" Left:")
69+
self._left.dump()
70+
print(" Right:")
71+
self._right.dump()
72+
73+
74+
if __name__ == "__main__":
75+
pkts = [
76+
Packet(flow=1, idn=1, length=200),
77+
Packet(flow=1, idn=2, length=200),
78+
Packet(flow=10, idn=1, length=200),
79+
Packet(flow=10, idn=2, length=200),
80+
Packet(flow=2, idn=1, length=100),
81+
Packet(flow=2, idn=2, length=100),
82+
Packet(flow=2, idn=3, length=100),
83+
Packet(flow=20, idn=1, length=100),
84+
Packet(flow=20, idn=2, length=100),
85+
Packet(flow=20, idn=3, length=100),
86+
]
87+
Runner(pkts, Hpfq()).run()

queue-exp/pifo_srpt.py

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -36,15 +36,15 @@
3636
along with this program. If not, see <http://www.gnu.org/licenses/>.
3737
"""
3838

39-
from pifo_lib import Packet, Runner, Pifo
40-
from pifo_lib import SchedulingAlgorithm
41-
from pifo_lib import FlowTracker
39+
from sched_lib import Packet, Runner, Pifo, SchedulingAlgorithm
40+
from sched_lib import FlowTracker
4241

4342

4443
class Srpt(SchedulingAlgorithm):
4544
"""Shortest Remaining Processing Time"""
4645

47-
def __init__(self):
46+
def __init__(self, name=None):
47+
super().__init__(name)
4848
self._pifo = Pifo()
4949
self._flow_tracker = FlowTracker()
5050

@@ -57,22 +57,24 @@ def __init__(self):
5757
else:
5858
self._remains[pkt.flow] = pkt.length
5959

60-
def get_rank(self, pkt):
61-
rank = self._remains[pkt.flow]
62-
self._remains[pkt.flow] -= pkt.length
60+
def get_rank(self, item):
61+
"""Rank the items by their remaining total flow length."""
62+
63+
rank = self._remains[item.flow]
64+
self._remains[item.flow] -= item.length
6365
return rank
6466

65-
def enqueue(self, item):
67+
def enqueue(self, ref, item):
6668
flow = self._flow_tracker.enqueue(item)
6769
rank = self.get_rank(item)
6870
self._pifo.enqueue(flow, rank)
6971

7072
def dequeue(self):
7173
flow = self._pifo.dequeue()
72-
pkt = None
74+
item = None
7375
if flow is not None:
74-
pkt = flow.dequeue()
75-
return pkt
76+
item = flow.dequeue()
77+
return item
7678

7779
def dump(self):
7880
self._pifo.dump()

queue-exp/pifo_stfq.py

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -34,31 +34,34 @@
3434
along with this program. If not, see <http://www.gnu.org/licenses/>.
3535
"""
3636

37-
from pifo_lib import Packet, Runner, Pifo
38-
from pifo_lib import SchedulingAlgorithm
37+
from sched_lib import Packet, Runner, Pifo, SchedulingAlgorithm
3938

4039

4140
class Stfq(SchedulingAlgorithm):
4241
"""Start-Time Fair Queuing (STFQ)"""
4342

44-
def __init__(self):
43+
def __init__(self, name=None):
44+
super().__init__(name)
4545
self._pifo = Pifo()
4646

4747
self._last_finish = {}
4848
self._virt_time = 0
4949

50-
def get_rank(self, pkt):
51-
flow_id = pkt.flow
50+
def get_rank(self, item):
51+
"""Rank the items by their start time, which we calculate from the
52+
finish time of the last packet.
53+
"""
54+
flow_id = item.flow
5255
if flow_id in self._last_finish:
5356
rank = max(self._virt_time, self._last_finish[flow_id])
5457
else:
5558
rank = self._virt_time
56-
self._last_finish[flow_id] = rank + pkt.length
59+
self._last_finish[flow_id] = rank + item.length
5760
return rank
5861

59-
def enqueue(self, item):
62+
def enqueue(self, ref, item):
6063
rank = self.get_rank(item)
61-
self._pifo.enqueue(item, rank)
64+
self._pifo.enqueue(ref, rank)
6265

6366
def dequeue(self):
6467
return self._pifo.dequeue()

queue-exp/pifo_wfq.py

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -34,31 +34,36 @@
3434
along with this program. If not, see <http://www.gnu.org/licenses/>.
3535
"""
3636

37-
from pifo_lib import Packet, Runner, Pifo
38-
from pifo_lib import SchedulingAlgorithm
37+
from sched_lib import Packet, Runner, Pifo, SchedulingAlgorithm
3938

4039

4140
class Wfq(SchedulingAlgorithm):
4241
"""Weighted Fair Queueing (WFQ)"""
4342

44-
def __init__(self):
43+
def __init__(self, name=None):
44+
super().__init__(name)
4545
self._pifo = Pifo()
4646
self._last_finish = {}
4747
self._virt_time = 0
4848

49-
def get_rank(self, pkt):
50-
flow = pkt.flow
49+
def get_rank(self, item):
50+
"""Rank the items by their start time, which we calculate from the
51+
finish time of the last packet. However, we divide the packet's length
52+
with weights to prioritize them. We determine the weights by splitting
53+
the flow-ids into odd and even numbers.
54+
"""
55+
flow = item.flow
5156
weight = 50 if flow % 2 == 1 else 100
5257
if flow in self._last_finish:
5358
rank = max(self._virt_time, self._last_finish[flow])
5459
else:
5560
rank = self._virt_time
56-
self._last_finish[flow] = rank + pkt.length / weight
61+
self._last_finish[flow] = rank + item.length / weight
5762
return rank
5863

59-
def enqueue(self, item):
64+
def enqueue(self, ref, item):
6065
rank = self.get_rank(item)
61-
self._pifo.enqueue(item, rank)
66+
self._pifo.enqueue(ref, rank)
6267

6368
def dequeue(self):
6469
return self._pifo.dequeue()

queue-exp/pifo_lib.py renamed to queue-exp/sched_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, ref, item):
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})"

0 commit comments

Comments
 (0)