paper(ipcq): add latency-model-style flow + stacked architecture diagrams
Adds two new IPCQ architecture views in the latency_model.png aesthetic: a 2x2 flow figure (one per case) and a 4x1 stacked figure for taller horizontal panels. Refactors per-case panel drawing into shared helpers and disambiguates the flit labels (HMQ desc -> Hd so it does not collide with data D). Two-row legend keys. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Binary file not shown.
|
After Width: | Height: | Size: 230 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 260 KiB |
@@ -238,6 +238,267 @@ def _plot_architecture() -> Path:
|
|||||||
return out
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
# Module-level flow-diagram helpers and constants.
|
||||||
|
_FLOW_DATA = "#6699CC" # blue flit (data)
|
||||||
|
_FLOW_CTRL = "#ED7D65" # orange flit (control)
|
||||||
|
_FLOW_CRED = "#7BB661" # green (credit / ack)
|
||||||
|
|
||||||
|
|
||||||
|
def _flow_swim(ax, x, y, w, h, label, sub=None):
|
||||||
|
ax.add_patch(mpatches.FancyBboxPatch(
|
||||||
|
(x, y), w, h,
|
||||||
|
boxstyle="round,pad=0.02,rounding_size=0.08",
|
||||||
|
facecolor="white", edgecolor="#3a3a3a", linewidth=1.6))
|
||||||
|
ax.text(x + w / 2, y + h / 2 + (0.20 if sub else 0),
|
||||||
|
label, ha="center", va="center",
|
||||||
|
fontsize=10.5, fontweight="bold", color="#111")
|
||||||
|
if sub:
|
||||||
|
ax.text(x + w / 2, y + h / 2 - 0.35, sub,
|
||||||
|
ha="center", va="center",
|
||||||
|
fontsize=8, color="#555", fontstyle="italic")
|
||||||
|
|
||||||
|
|
||||||
|
def _flow_flit(ax, x, y, color, letter="", size=0.32):
|
||||||
|
ax.add_patch(mpatches.FancyBboxPatch(
|
||||||
|
(x - size / 2, y - size / 2), size, size,
|
||||||
|
boxstyle="round,pad=0.01,rounding_size=0.04",
|
||||||
|
facecolor=color, edgecolor="#222", linewidth=0.8))
|
||||||
|
if letter:
|
||||||
|
ax.text(x, y, letter, ha="center", va="center",
|
||||||
|
fontsize=8.5, fontweight="bold", color="white")
|
||||||
|
|
||||||
|
|
||||||
|
def _flow_wire(ax, x1, x2, y, color="#888"):
|
||||||
|
ax.plot([x1, x2], [y, y], color=color, lw=1.0, zorder=0)
|
||||||
|
|
||||||
|
|
||||||
|
def _flow_dot(ax, x, y, color):
|
||||||
|
ax.add_patch(mpatches.Circle((x, y), 0.13,
|
||||||
|
facecolor=color, edgecolor="#333",
|
||||||
|
linewidth=0.8, zorder=4))
|
||||||
|
|
||||||
|
|
||||||
|
def _flow_label(ax, x, y, text, color="#222", fontsize=9):
|
||||||
|
ax.text(x, y, text, ha="center", va="center",
|
||||||
|
fontsize=fontsize, color=color, fontweight="bold")
|
||||||
|
|
||||||
|
|
||||||
|
def _flow_lead(ax, x_tail, y_tail, x_head, y_head, color="#777"):
|
||||||
|
ax.plot([x_tail, x_head], [y_tail, y_head],
|
||||||
|
color=color, lw=0.8, ls=":", zorder=2)
|
||||||
|
|
||||||
|
|
||||||
|
def _flow_setup(ax, kind):
|
||||||
|
ax.set_xlim(0, 20); ax.set_ylim(0, 10); ax.axis("off")
|
||||||
|
ax.add_patch(mpatches.FancyBboxPatch(
|
||||||
|
(0.15, 0.15), 19.7, 9.7,
|
||||||
|
boxstyle="round,pad=0.05,rounding_size=0.12",
|
||||||
|
facecolor="white", edgecolor=_COLOR[kind],
|
||||||
|
linewidth=2.2, zorder=0))
|
||||||
|
ax.text(10, 0.65, _TITLE[kind],
|
||||||
|
ha="center", va="center", fontsize=11.5,
|
||||||
|
fontweight="bold",
|
||||||
|
color=("#1B5E20" if kind == "ipcq" else "white"),
|
||||||
|
bbox=dict(
|
||||||
|
facecolor=("#E8F5E9" if kind == "ipcq" else _COLOR[kind]),
|
||||||
|
edgecolor=("#2E7D32" if kind == "ipcq" else _COLOR[kind]),
|
||||||
|
boxstyle="round,pad=0.40", linewidth=1.5))
|
||||||
|
|
||||||
|
|
||||||
|
def _draw_flow_case(ax, kind):
|
||||||
|
"""Draw one Sender → NoC → Receiver flow panel for `kind`."""
|
||||||
|
_flow_setup(ax, kind)
|
||||||
|
if kind == "doorbell":
|
||||||
|
_draw_case_doorbell(ax)
|
||||||
|
elif kind == "hmq":
|
||||||
|
_draw_case_hmq(ax)
|
||||||
|
elif kind == "rdma":
|
||||||
|
_draw_case_rdma(ax)
|
||||||
|
elif kind == "ipcq":
|
||||||
|
_draw_case_ipcq(ax)
|
||||||
|
|
||||||
|
|
||||||
|
def _draw_case_doorbell(ax):
|
||||||
|
_flow_swim(ax, 0.7, 4.4, 2.8, 2.0, "Sender\nkernel")
|
||||||
|
_flow_swim(ax, 7.4, 4.4, 3.0, 2.0, "NoC / UCIe", sub="2 DMA tx")
|
||||||
|
_flow_swim(ax, 14.4, 4.4, 3.0, 2.0, "Doorbell reg\n+ landing TCM")
|
||||||
|
_flow_swim(ax, 17.6, 6.7, 2.0, 1.7, "Receiver\n(poll)", sub="poll loop")
|
||||||
|
_flow_wire(ax, 3.5, 7.4, 5.7); _flow_wire(ax, 10.4, 14.4, 5.7)
|
||||||
|
for x in [4.0, 4.7, 5.4]:
|
||||||
|
_flow_flit(ax, x, 5.7, _FLOW_DATA, "D")
|
||||||
|
for x in [11.0, 11.7]:
|
||||||
|
_flow_flit(ax, x, 5.7, _FLOW_DATA, "D")
|
||||||
|
_flow_wire(ax, 3.5, 7.4, 5.1); _flow_wire(ax, 10.4, 14.4, 5.1)
|
||||||
|
_flow_flit(ax, 4.0, 5.1, _FLOW_CTRL, "F")
|
||||||
|
_flow_flit(ax, 4.7, 5.1, _FLOW_CTRL, "DB")
|
||||||
|
_flow_flit(ax, 11.0, 5.1, _FLOW_CTRL, "DB")
|
||||||
|
for y in (8.0, 7.5, 7.0):
|
||||||
|
ax.annotate("", xy=(15.5, y), xytext=(17.6, y),
|
||||||
|
arrowprops=dict(arrowstyle="->", color="#aa3a30",
|
||||||
|
lw=1.0, ls=":"))
|
||||||
|
_flow_dot(ax, 5.0, 3.5, "#c5a35a")
|
||||||
|
_flow_lead(ax, 5.0, 3.6, 5.0, 4.4)
|
||||||
|
_flow_label(ax, 5.0, 3.2, "fence + 2 DMA writes", color="#5a3e20")
|
||||||
|
_flow_dot(ax, 17.0, 8.8, "#7BB661")
|
||||||
|
_flow_lead(ax, 17.0, 8.7, 17.0, 8.0)
|
||||||
|
_flow_label(ax, 17.0, 9.1, "receiver polling loop", color="#2E7D32")
|
||||||
|
|
||||||
|
|
||||||
|
def _draw_case_hmq(ax):
|
||||||
|
_flow_swim(ax, 0.7, 4.4, 2.8, 2.0, "Sender CPU")
|
||||||
|
_flow_swim(ax, 4.6, 6.6, 2.8, 1.8, "HMQ engine", sub="descriptor")
|
||||||
|
_flow_swim(ax, 4.6, 3.6, 2.8, 1.8, "PE_DMA", sub="data path")
|
||||||
|
_flow_swim(ax, 9.6, 4.4, 3.0, 2.0, "NoC / UCIe", sub="2 paths")
|
||||||
|
_flow_swim(ax, 14.4, 6.6, 2.8, 1.8, "Peer HMQ")
|
||||||
|
_flow_swim(ax, 14.4, 3.6, 2.8, 1.8, "Recv TCM")
|
||||||
|
_flow_swim(ax, 17.6, 4.4, 2.0, 2.0, "Receiver\nCPU")
|
||||||
|
_flow_wire(ax, 3.5, 4.6, 7.5); _flow_wire(ax, 7.4, 9.6, 7.5)
|
||||||
|
_flow_wire(ax, 12.6, 14.4, 7.5); _flow_wire(ax, 17.2, 17.6, 5.4)
|
||||||
|
_flow_flit(ax, 4.0, 7.5, _FLOW_CTRL, "Hd")
|
||||||
|
_flow_flit(ax, 8.5, 7.5, _FLOW_CTRL, "Hd")
|
||||||
|
_flow_flit(ax, 13.5, 7.5, _FLOW_CTRL, "Hd")
|
||||||
|
_flow_wire(ax, 3.5, 4.6, 4.5); _flow_wire(ax, 7.4, 9.6, 4.5)
|
||||||
|
_flow_wire(ax, 12.6, 14.4, 4.5)
|
||||||
|
for x in [4.0, 8.0, 8.7, 13.5]:
|
||||||
|
_flow_flit(ax, x, 4.5, _FLOW_DATA, "D")
|
||||||
|
_flow_dot(ax, 11.1, 9.0, "#c5a35a")
|
||||||
|
_flow_lead(ax, 11.1, 8.9, 11.1, 7.6)
|
||||||
|
_flow_label(ax, 11.1, 9.25, "duplicated control + data paths",
|
||||||
|
color="#5a3e20")
|
||||||
|
|
||||||
|
|
||||||
|
def _draw_case_rdma(ax):
|
||||||
|
_flow_swim(ax, 0.7, 4.4, 2.8, 2.0, "Sender\nkernel")
|
||||||
|
_flow_swim(ax, 4.8, 4.4, 2.8, 2.0, "WQE +\nPE_DMA")
|
||||||
|
_flow_swim(ax, 8.5, 4.4, 3.0, 2.0, "NoC / UCIe")
|
||||||
|
_flow_swim(ax, 13.0, 4.4, 3.0, 2.0, "CQ + landing\nTCM",
|
||||||
|
sub="auto-CQE")
|
||||||
|
_flow_swim(ax, 17.0, 6.7, 2.5, 1.7, "Receiver\n(poll CQ)")
|
||||||
|
_flow_wire(ax, 3.5, 4.8, 5.7)
|
||||||
|
_flow_flit(ax, 4.0, 5.7, _FLOW_CTRL, "W")
|
||||||
|
_flow_wire(ax, 7.6, 8.5, 5.4); _flow_wire(ax, 11.5, 13.0, 5.4)
|
||||||
|
_flow_flit(ax, 8.1, 5.4, _FLOW_DATA, "D")
|
||||||
|
_flow_flit(ax, 9.2, 5.4, _FLOW_DATA, "D")
|
||||||
|
_flow_flit(ax, 9.9, 5.4, _FLOW_CTRL, "Q")
|
||||||
|
_flow_flit(ax, 11.0, 5.4, _FLOW_DATA, "D")
|
||||||
|
_flow_flit(ax, 12.0, 5.4, _FLOW_CTRL, "Q")
|
||||||
|
for y in (8.0, 7.5, 7.0):
|
||||||
|
ax.annotate("", xy=(14.5, y), xytext=(17.0, y),
|
||||||
|
arrowprops=dict(arrowstyle="->", color="#7c1b78",
|
||||||
|
lw=1.0, ls=":"))
|
||||||
|
_flow_dot(ax, 11.0, 3.5, "#c5a35a")
|
||||||
|
_flow_lead(ax, 11.0, 3.6, 11.0, 4.4)
|
||||||
|
_flow_label(ax, 11.0, 3.2, "CQE rides with DMA payload",
|
||||||
|
color="#5a3e20")
|
||||||
|
_flow_dot(ax, 18.0, 8.8, "#7BB661")
|
||||||
|
_flow_lead(ax, 18.0, 8.7, 18.0, 8.0)
|
||||||
|
_flow_label(ax, 18.0, 9.1, "CQ poll / IRQ", color="#2E7D32")
|
||||||
|
|
||||||
|
|
||||||
|
def _draw_case_ipcq(ax):
|
||||||
|
_flow_swim(ax, 0.7, 4.4, 2.8, 2.0, "Sender PE\n(tl.send)")
|
||||||
|
_flow_swim(ax, 4.5, 6.6, 3.0, 1.8, "PE_IPCQ", sub="ring push")
|
||||||
|
_flow_swim(ax, 4.5, 3.6, 3.0, 1.8, "PE_DMA", sub="data push")
|
||||||
|
_flow_swim(ax, 9.4, 4.4, 3.0, 2.0, "NoC / UCIe", sub="dir-addr")
|
||||||
|
_flow_swim(ax, 13.8, 6.6, 3.0, 1.8, "PE_IPCQ",
|
||||||
|
sub="tail++ , credit")
|
||||||
|
_flow_swim(ax, 13.8, 3.6, 3.0, 1.8, "PE_DMA",
|
||||||
|
sub="landing TCM")
|
||||||
|
_flow_swim(ax, 17.6, 4.4, 2.0, 2.0, "Receiver\nPE\n(tl.recv)")
|
||||||
|
_flow_wire(ax, 3.5, 4.5, 7.5); _flow_wire(ax, 7.5, 9.4, 7.5)
|
||||||
|
_flow_wire(ax, 12.4, 13.8, 7.5); _flow_wire(ax, 16.8, 17.6, 5.4)
|
||||||
|
_flow_flit(ax, 4.0, 7.5, _FLOW_CTRL, "M")
|
||||||
|
_flow_flit(ax, 8.4, 7.5, _FLOW_CTRL, "M")
|
||||||
|
_flow_flit(ax, 13.0, 7.5, _FLOW_CTRL, "M")
|
||||||
|
_flow_wire(ax, 3.5, 4.5, 4.5); _flow_wire(ax, 7.5, 9.4, 4.5)
|
||||||
|
_flow_wire(ax, 12.4, 13.8, 4.5)
|
||||||
|
_flow_flit(ax, 4.0, 4.5, _FLOW_DATA, "D")
|
||||||
|
_flow_flit(ax, 8.4, 4.5, _FLOW_DATA, "D")
|
||||||
|
_flow_flit(ax, 11.0, 4.5, _FLOW_DATA, "D")
|
||||||
|
_flow_flit(ax, 13.0, 4.5, _FLOW_DATA, "D")
|
||||||
|
ax.annotate("", xy=(4.5, 9.0), xytext=(16.8, 9.0),
|
||||||
|
arrowprops=dict(arrowstyle="->", color=_FLOW_CRED,
|
||||||
|
lw=1.6, ls=(0, (5, 3))))
|
||||||
|
_flow_label(ax, 10.5, 9.35, "credit piggyback (no separate ACK)",
|
||||||
|
color=_FLOW_CRED)
|
||||||
|
_flow_dot(ax, 6.0, 2.6, "#c5a35a")
|
||||||
|
_flow_lead(ax, 6.0, 2.7, 6.0, 3.6)
|
||||||
|
_flow_label(ax, 6.0, 2.3, "one in-line ring push + one DMA",
|
||||||
|
color="#5a3e20")
|
||||||
|
|
||||||
|
|
||||||
|
def _flow_legend(fig, leg_y=0.02):
|
||||||
|
"""Two-row legend so the long control-flit key doesn't overlap
|
||||||
|
the shorter data/credit entries."""
|
||||||
|
row_top = [
|
||||||
|
(_FLOW_DATA, "Data flit D = payload"),
|
||||||
|
(_FLOW_CRED, "Credit / piggyback return"),
|
||||||
|
]
|
||||||
|
row_bottom = [
|
||||||
|
(_FLOW_CTRL, "Control flit: W=WQE · Q=CQE · M=ring meta"
|
||||||
|
" · DB=doorbell · F=fence · Hd=HMQ desc"),
|
||||||
|
]
|
||||||
|
|
||||||
|
def _emit(items, y):
|
||||||
|
lx = 0.04
|
||||||
|
for color, text in items:
|
||||||
|
fig.patches.append(mpatches.FancyBboxPatch(
|
||||||
|
(lx, y), 0.020, 0.022,
|
||||||
|
boxstyle="round,pad=0.005,rounding_size=0.005",
|
||||||
|
facecolor=color, edgecolor="#222", linewidth=0.6,
|
||||||
|
transform=fig.transFigure))
|
||||||
|
fig.text(lx + 0.025, y + 0.011, text,
|
||||||
|
ha="left", va="center", fontsize=10,
|
||||||
|
fontweight="bold", color="#222")
|
||||||
|
lx += 0.32
|
||||||
|
|
||||||
|
_emit(row_top, y=leg_y + 0.030)
|
||||||
|
_emit(row_bottom, y=leg_y)
|
||||||
|
|
||||||
|
|
||||||
|
# ─── Diagram 1b : 4-case flow-style architecture (latency_model look)
|
||||||
|
def _plot_architecture_flow() -> Path:
|
||||||
|
"""4 panels arranged 2×2 — each a horizontal Sender → NoC → Receiver
|
||||||
|
flow with small flit-style packets, dot-marker annotations and a
|
||||||
|
bottom name plate. Aesthetic mirrors latency_model.png."""
|
||||||
|
fig, axes = plt.subplots(2, 2, figsize=(18.0, 11.5))
|
||||||
|
_draw_flow_case(axes[0, 0], "doorbell")
|
||||||
|
_draw_flow_case(axes[0, 1], "hmq")
|
||||||
|
_draw_flow_case(axes[1, 0], "rdma")
|
||||||
|
_draw_flow_case(axes[1, 1], "ipcq")
|
||||||
|
fig.suptitle(
|
||||||
|
"Per-send architecture = $\\Sigma$ control overhead + "
|
||||||
|
"data-path DMA + receiver wake",
|
||||||
|
fontsize=14.5, fontweight="bold", y=0.995)
|
||||||
|
_flow_legend(fig, leg_y=0.015)
|
||||||
|
fig.tight_layout(rect=(0, 0.07, 1, 0.96))
|
||||||
|
out = _OUT_DIR / "ipcq_alternatives_architecture_flow.png"
|
||||||
|
fig.savefig(out, dpi=150, bbox_inches="tight")
|
||||||
|
plt.close(fig)
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
# ─── Diagram 1c : 4-case flow-style architecture — stacked 4×1 ──────
|
||||||
|
def _plot_architecture_stacked() -> Path:
|
||||||
|
"""Same per-case flow content as the 2×2 figure, but stacked one
|
||||||
|
case per row (4 rows × 1 column). Wider panels give each case more
|
||||||
|
horizontal room."""
|
||||||
|
fig, axes = plt.subplots(4, 1, figsize=(16.0, 19.0))
|
||||||
|
for ax, kind in zip(axes, ["doorbell", "hmq", "rdma", "ipcq"]):
|
||||||
|
_draw_flow_case(ax, kind)
|
||||||
|
fig.suptitle(
|
||||||
|
"Per-send architecture = $\\Sigma$ control overhead + "
|
||||||
|
"data-path DMA + receiver wake",
|
||||||
|
fontsize=14.5, fontweight="bold", y=0.997)
|
||||||
|
_flow_legend(fig, leg_y=0.010)
|
||||||
|
fig.tight_layout(rect=(0, 0.05, 1, 0.97))
|
||||||
|
out = _OUT_DIR / "ipcq_alternatives_architecture_stacked.png"
|
||||||
|
fig.savefig(out, dpi=150, bbox_inches="tight")
|
||||||
|
plt.close(fig)
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# ─── Diagram 2 : Per-send latency breakdown (vertical stacks) ───────
|
# ─── Diagram 2 : Per-send latency breakdown (vertical stacks) ───────
|
||||||
def _plot_latency() -> Path:
|
def _plot_latency() -> Path:
|
||||||
# (label, short, illustrative cycles, fill)
|
# (label, short, illustrative cycles, fill)
|
||||||
@@ -782,7 +1043,9 @@ def _plot_control_data_overlay() -> Path:
|
|||||||
|
|
||||||
def main() -> None:
|
def main() -> None:
|
||||||
_OUT_DIR.mkdir(parents=True, exist_ok=True)
|
_OUT_DIR.mkdir(parents=True, exist_ok=True)
|
||||||
for plot in (_plot_architecture, _plot_latency,
|
for plot in (_plot_architecture, _plot_architecture_flow,
|
||||||
|
_plot_architecture_stacked,
|
||||||
|
_plot_latency,
|
||||||
_plot_opcounts, _plot_sequence,
|
_plot_opcounts, _plot_sequence,
|
||||||
_plot_decision_matrix,
|
_plot_decision_matrix,
|
||||||
_plot_control_data_overlay):
|
_plot_control_data_overlay):
|
||||||
|
|||||||
BIN
Binary file not shown.
|
After Width: | Height: | Size: 230 KiB |
BIN
Binary file not shown.
|
After Width: | Height: | Size: 260 KiB |
Reference in New Issue
Block a user