Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Queues: benchmark implementations against each other #2276

Open
wants to merge 19 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion calyx-py/calyx/builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ def case(
width = self.infer_width(signal)
ifs = []
for branch, controllable in cases.items():
std_eq = self.eq(width, f"{signal.name}_eq_{branch}", signed)
std_eq = self.eq(width, self.generate_name(f"{signal.name}_eq_{branch}"), signed)

with self.continuous:
std_eq.left = signal
Expand Down
16 changes: 8 additions & 8 deletions calyx-py/test/case.expect
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,24 @@ import "primitives/binary_operators.futil";
component my_comp(in_1: 8) -> (out_1: 16) {
cells {
comp_reg = std_reg(1);
in_1_eq_1 = std_eq(8);
in_1_eq_2 = std_eq(8);
in_1_eq_1_1 = std_eq(8);
in_1_eq_2_2 = std_eq(8);
}
wires {
group my_group {

}
in_1_eq_1.left = in_1;
in_1_eq_1.right = 8'd1;
in_1_eq_2.left = in_1;
in_1_eq_2.right = 8'd2;
in_1_eq_1_1.left = in_1;
in_1_eq_1_1.right = 8'd1;
in_1_eq_2_2.left = in_1;
in_1_eq_2_2.right = 8'd2;
}
control {
par {
if in_1_eq_1.out {
if in_1_eq_1_1.out {
my_group;
}
if in_1_eq_2.out {
if in_1_eq_2_2.out {
invoke comp_reg(in=1'd1)();
}
}
Expand Down
19 changes: 19 additions & 0 deletions frontends/queues/cycles.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#!/usr/bin/bash

shopt -s globstar

cd "$(dirname "$0")/../.." # move to root

for file in frontends/queues/tests/**/*.py; do
name="$(basename $file .py)"
dir="$(dirname $file)"

cycles="$(python3 $file 20000 --keepgoing |\
fud e --from calyx --to jq \
--through verilog \
--through dat \
-s verilog.data "$dir/$name.data" \
-s jq.expr ".cycles" \
-q)"
echo "${file#*tests/}: $cycles"
done
30 changes: 30 additions & 0 deletions frontends/queues/cycles.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
binheap/binheap_test.py: 750
binheap/fifo_test.py: 1509164
binheap/pifo_test.py: 1784719
binheap/round_robin/rr_2flow_test.py: 1870740
binheap/round_robin/rr_3flow_test.py: 1884450
binheap/round_robin/rr_4flow_test.py: 1897807
binheap/round_robin/rr_5flow_test.py: 1903303
binheap/round_robin/rr_6flow_test.py: 1934811
binheap/round_robin/rr_7flow_test.py: 1944544
binheap/stable_binheap_test.py: 1802173
binheap/strict/strict_2flow_test.py: 1785391
binheap/strict/strict_3flow_test.py: 1826179
binheap/strict/strict_4flow_test.py: 1842823
binheap/strict/strict_5flow_test.py: 1852314
binheap/strict/strict_6flow_test.py: 1851588
complex_tree_test.py: 1504412
fifo_test.py: 595422
pifo_tree_test.py: 1199525
round_robin/rr_2flow_test.py: 993313
round_robin/rr_3flow_test.py: 1013489
round_robin/rr_4flow_test.py: 1032869
round_robin/rr_5flow_test.py: 1051211
round_robin/rr_6flow_test.py: 1125919
round_robin/rr_7flow_test.py: 1136933
sdn_test.py: 1244054
strict/strict_2flow_test.py: 1058077
strict/strict_3flow_test.py: 1144637
strict/strict_4flow_test.py: 1233029
strict/strict_5flow_test.py: 1315433
strict/strict_6flow_test.py: 1481437
Binary file added frontends/queues/cycles_round_robin.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added frontends/queues/cycles_strict.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added frontends/queues/lut_round_robin.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added frontends/queues/lut_strict.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added frontends/queues/muxes_round_robin.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added frontends/queues/muxes_strict.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
122 changes: 122 additions & 0 deletions frontends/queues/plot.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import os
import sys
import json
import numpy as np
import matplotlib.pyplot as plt

stat = sys.argv[1]

def append_path_prefix(file):
path_to_script = os.path.dirname(__file__)
path_to_file = os.path.join(path_to_script, file)
return path_to_file

def parse(stat, file):
binheap_rr = []
specialized_rr = []
binheap_strict = []
specialized_strict = []

with open(file) as file:
if stat == "cycles":
for line in file:
split = line.strip().split(":")
tup = (split[0].split("flow")[0][-1], int(split[1]))
if "round_robin" in line:
if "binheap" in line:
binheap_rr.append(tup)
else:
specialized_rr.append(tup)
if "strict" in line:
if "binheap" in line:
binheap_strict.append(tup)
else:
specialized_strict.append(tup)
else:
data = file.read().strip()
for d in data.split("\n\n"):
split = d.split("\n")
name = split[0]
stats = json.loads("\n".join(split[1:]))
tup = (name.split("flow")[0][-1], stats[stat])
if "round_robin" in name:
if "binheap" in name:
binheap_rr.append(tup)
else:
specialized_rr.append(tup)
if "strict" in name:
if "binheap" in name:
binheap_strict.append(tup)
else:
specialized_strict.append(tup)

return (specialized_rr,
binheap_rr,
specialized_strict,
binheap_strict)

def draw(specialized, binheap, name, filename, details=None):
fig, ax = plt.subplots(1, 1)
fig.set_size_inches(20, 10, forward=True)
ax.set_title(name,
fontweight='bold',
fontsize=20)
ax.set_xlabel("number of flows",
fontsize=20)
if details is None:
ax.set_ylabel(stat,
fontsize=20)
else:
ax.set_ylabel(f"{stat} ({details})",
fontsize=20)
specialized = ax.scatter(
list(map(lambda x: x[0], specialized)),
list(map(lambda x: x[1], specialized)),
c='b')
binheap = ax.scatter(
list(map(lambda x: x[0], binheap)),
list(map(lambda x: x[1], binheap)),
c='g')
plt.legend((specialized, binheap),
("Specialized (i.e. Cassandra style PIFO)", "Binary Heap"),
fontsize=12)
file = append_path_prefix(f"{stat}_{filename}")
plt.savefig(file)
print(f"Generated {file}.png")

# Parse data for round_robin and strict queues
(specialized_rr, binheap_rr, specialized_strict, binheap_strict) = ([], [], [], [])
if stat == "total_time":
file1 = sys.argv[2]
file2 = sys.argv[3]

(specialized_cycles_rr,
binheap_cycles_rr,
specialized_cycles_strict,
binheap_cycles_strict) = parse("cycles", file1)
(specialized_slacks_rr,
binheap_slacks_rr,
specialized_slacks_strict,
binheap_slacks_strict) = parse("worst_slack", file2)

def map2(cycles, slacks):
cycles.sort(key=lambda c: c[0])
slacks.sort(key=lambda s: s[0])

def f(c,s):
return (c[0], (1000*c[1])/(7 - s[1]))

return list(map(f, cycles, slacks))

specialized_rr = map2(specialized_cycles_rr, specialized_slacks_rr)
binheap_rr = map2(binheap_cycles_rr, binheap_slacks_rr)
specialized_strict = map2(specialized_cycles_strict, specialized_slacks_strict)
binheap_strict = map2(binheap_cycles_strict, binheap_slacks_strict)
else:
file = sys.argv[2]
(specialized_rr, binheap_rr, specialized_strict, binheap_strict) = parse(stat, file)

# Draw results
details = "μs" if stat == "total_time" else None
draw(specialized_rr, binheap_rr, "Round Robin Queues", "round_robin", details)
draw(specialized_strict, binheap_strict, "Strict Queues", "strict", details)
2 changes: 2 additions & 0 deletions frontends/queues/queues/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,6 @@
import queues.binheap.stable_binheap as stable_binheap
import queues.binheap.fifo as binheap_fifo
import queues.binheap.pifo as binheap_pifo
import queues.binheap.round_robin as binheap_rr
import queues.binheap.strict as binheap_strict
import queues.binheap.binheap as binheap
33 changes: 33 additions & 0 deletions frontends/queues/queues/binheap/flow_inference.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# pylint: disable=import-error
import calyx.builder as cb


def insert_flow_inference(comp, value, flow, boundaries, name):
bound_checks = []

for b in range(len(boundaries)):
lt = comp.lt(32)
le = comp.le(32)
guard = comp.and_(1)

with comp.comb_group(f"{name}_bound_check_{b}") as bound_check_b:
le.left = value
le.right = boundaries[b]
if b > 0:
lt.left = boundaries[b-1]
lt.right = value
else:
lt.left = 0
lt.right = 1
guard.left = le.out
guard.right = lt.out

set_flow_b = comp.reg_store(flow, b, f"{name}_set_flow_{b}")
bound_check = cb.if_with(
cb.CellAndGroup(guard, bound_check_b),
set_flow_b
)

bound_checks.append(bound_check)

return cb.par(*bound_checks)
122 changes: 122 additions & 0 deletions frontends/queues/queues/binheap/round_robin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
# pylint: disable=import-error
import calyx.builder as cb
from calyx.utils import bits_needed
from queues.binheap.stable_binheap import insert_stable_binheap
from queues.binheap.flow_inference import insert_flow_inference

FACTOR = 4


def insert_binheap_rr(prog, name, boundaries, queue_size_factor=FACTOR):
n = len(boundaries)

comp = prog.component(name)

binheap = insert_stable_binheap(prog, "binheap", queue_size_factor)
binheap = comp.cell("binheap", binheap)

cmd = comp.input("cmd", 1)
value = comp.input("value", 32)

ans = comp.reg(32, "ans", is_ref=True)
err = comp.reg(1, "err", is_ref=True)

err_eq_0 = comp.eq_use(err.out, 0)

flow_in = comp.reg(bits_needed(n - 1), "flow_in")
infer_flow_in = insert_flow_inference(
comp, value, flow_in, boundaries, "infer_flow_in"
)

flow_out = comp.reg(bits_needed(n - 1), "flow_out")
infer_flow_out = insert_flow_inference(
comp, ans.out, flow_out, boundaries, "infer_flow_out"
)

rank_ptrs = [comp.reg(32, f"r_{i}") for i in range(n)]
rank_ptr_incrs = dict([(i, comp.incr(rank_ptrs[i], n)) for i in range(n)])

turn = comp.reg(bits_needed(n - 1), "turn")
turn_neq_flow_out = comp.neq_use(turn.out, flow_out.out)
turn_incr_mod_n = cb.if_with(
comp.eq_use(turn.out, n - 1),
comp.reg_store(turn, 0),
comp.incr(turn)
)

init = comp.reg(1, "init")
init_eq_0 = comp.eq_use(init.out, 0)
init_state = cb.if_with(
init_eq_0,
[
cb.par(*[ comp.reg_store(rank_ptrs[i], i) for i in range(n) ]),
comp.incr(init)
]
)

def binheap_invoke(value, rank):
return cb.invoke(
binheap,
in_value=value,
in_rank=rank,
in_cmd=cmd,
ref_ans=ans,
ref_err=err,
)
binheap_invokes = dict([
(i, binheap_invoke(value, rank_ptrs[i].out))
for i in range(n)
])

comp.control += [
init_state,
infer_flow_in,
comp.case(flow_in.out, binheap_invokes),
cb.if_with(
err_eq_0,
comp.case(
cmd,
{
0:
[
infer_flow_out,
cb.while_with(
turn_neq_flow_out,
[
comp.case(turn.out, rank_ptr_incrs),
turn_incr_mod_n
]
),
turn_incr_mod_n
],
1:
comp.case(flow_in.out, rank_ptr_incrs)
}
)
)
]

return comp


def generate(prog, numflows):
"""Generates queue with specific `boundaries`"""

if numflows == 2:
boundaries = [200, 400]
elif numflows == 3:
boundaries = [133, 266, 400]
elif numflows == 4:
boundaries = [100, 200, 300, 400]
elif numflows == 5:
boundaries = [80, 160, 240, 320, 400]
elif numflows == 6:
boundaries = [66, 100, 200, 220, 300, 400]
elif numflows == 7:
boundaries = [50, 100, 150, 200, 250, 300, 400]
else:
raise ValueError("Unsupported number of flows")

pifo = insert_binheap_rr(prog, "pifo", boundaries)

return pifo
Loading
Loading