Merge branch 'linus/updated_analysis_v2' into 'main'
updated create csv and updated plots See merge request course-asl-fastcode26/team70!53
|
Before Width: | Height: | Size: 148 KiB After Width: | Height: | Size: 110 KiB |
|
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 90 KiB |
|
After Width: | Height: | Size: 104 KiB |
|
After Width: | Height: | Size: 381 KiB |
|
Before Width: | Height: | Size: 105 KiB After Width: | Height: | Size: 102 KiB |
@@ -27,6 +27,14 @@ def num(pattern, text):
|
||||
#min cycles is the standard way to kill the OS/turbo noise.
|
||||
best = {}
|
||||
|
||||
# flop counter is uint32 and wraps at n=10000 (72*n^2+74*n = 7.2e9 > 2^32), so the logged
|
||||
# W(n)/I(n) are garbage there. compute work ourselves; matches the good logs exactly.
|
||||
# symmetry variants do half the pairs -> 36 n^2 + 110 n, everything else 72 n^2 + 74 n.
|
||||
def flops_per_iter(variant, n):
|
||||
if "symmetry" in variant:
|
||||
return 36 * n * n + 110 * n
|
||||
return 72 * n * n + 74 * n
|
||||
|
||||
for variant in sorted(os.listdir(ROOT)):
|
||||
logdir = os.path.join(ROOT, variant, "x86")
|
||||
if not os.path.isdir(logdir):
|
||||
@@ -48,20 +56,20 @@ for variant in sorted(os.listdir(ROOT)):
|
||||
cycles = num(r"T\(n\):\s*([\d.]+)", block)
|
||||
if cycles is None:
|
||||
continue
|
||||
W = num(r"W\(n\):\s*([\d.]+)", block)
|
||||
|
||||
key = (variant, n)
|
||||
if key not in best or cycles < best[key][2]:
|
||||
best[key] = (variant, n, cycles, n_iter, W,
|
||||
num(r"Q\(n\) per iteration:\s*([\d.]+)", block),
|
||||
num(r"I\(n\):\s*([\d.]+)", block))
|
||||
if key not in best or cycles < best[key][0]:
|
||||
best[key] = (cycles, n_iter,
|
||||
num(r"Q\(n\) per iteration:\s*([\d.]+)", block))
|
||||
|
||||
with open(OUT, "w", newline="") as f:
|
||||
w = csv.writer(f)
|
||||
w.writerow(["variant", "n", "n_iter", "W", "Q_per_iter", "I", "cycles", "flops_per_cycle"])
|
||||
for variant, n in sorted(best):
|
||||
_, _, cycles, n_iter, W, q, I = best[(variant, n)]
|
||||
cycles, n_iter, q = best[(variant, n)]
|
||||
w_iter = flops_per_iter(variant, n)
|
||||
W = w_iter * n_iter
|
||||
I = w_iter / q
|
||||
fc = W / cycles
|
||||
w.writerow([variant, n, n_iter, W, q, I, round(cycles, 1), round(fc, 3)])
|
||||
|
||||
w.writerow([variant, n, n_iter, W, q, round(I, 2), round(cycles, 1), round(fc, 3)])
|
||||
print("wrote", OUT, "with", len(best), "rows")
|
||||
@@ -81,13 +81,14 @@ base = get("variant_baseline")
|
||||
# plot 1: the two precision tracks (this is the main results plot)
|
||||
# double tops out ~7, float goes to 24. also shows the avx2-float stuff already does most of the work
|
||||
tracks = [
|
||||
("variant_inlining_7_SoA_AVX2", "double, AVX2", "#9bbbe0", "--"),
|
||||
("variant_baseline", "baseline (scalar)", "#7a7a7a", "-"),
|
||||
("variant_AVX512_1", "double, AVX-512 ", "#4f86d6", "-"),
|
||||
("variant_inlining_7_SoA_AVX2", "double, AVX-256", "#9bbbe0", "--"),
|
||||
("variant_inlining_8_SoA_AVX512_polyexp_moreavx", "double, AVX-512 + polyexp", "#2f5d9e", "-"),
|
||||
("variant_float_inlining_SoA_AVX2_polyexp", "float, AVX2 + polyexp", "#e58aa6", "--"),
|
||||
("variant_float_inlining_SoA_AVX2_polyexp_rsqrt_loopopt", "float, AVX2 + polyexp + rsqrt", "#d05a82", "--"),
|
||||
("variant_float_inlining_SoA_AVX2_polyexp", "float, AVX-256 + polyexp", "#e58aa6", "--"),
|
||||
("variant_float_inlining_SoA_AVX2_polyexp_rsqrt_loopopt", "float, AVX-256 + polyexp + rsqrt", "#d05a82", "--"),
|
||||
("variant_float_inlining_SoA_AVX512_polyexp_moreavx_ilp_2_rsqrt_morersqrt", "float, AVX-512 + rsqrt + ILP (best)", maroon, "-"),
|
||||
]
|
||||
|
||||
fig, ax = plt.subplots(figsize=(9,4.7))
|
||||
ends = []
|
||||
for v,lab,c,ls in tracks:
|
||||
@@ -144,43 +145,53 @@ plt.tight_layout()
|
||||
plt.savefig("analysis/fig_ladder.png", dpi=170, bbox_inches="tight")
|
||||
|
||||
|
||||
# plot3 roofline
|
||||
# every variant has the same I at a given n (W and Q dont change between variants), so the
|
||||
# points just stack vertically. we're miles to the right of the ridge -> compute bound.
|
||||
fig,ax = plt.subplots(figsize=(9,4.7))
|
||||
bw = 60 # bytes/cycle, ~120 GB/s lpddr5x / 2GHz. rough
|
||||
xx = np.logspace(-1, 3.6, 200)
|
||||
ax.plot(xx, np.minimum(bw*xx, PEAK_F), color="#cccccc", lw=1.2)
|
||||
# plot 3: roofline (n = 5000). same 7 variants as fig_tracks, same colors, inline labels.
|
||||
# float variants move HALF the bytes -> 2x the operational intensity of the double ones
|
||||
# (I_double ≈ 2.8k, I_float ≈ 5.6k), so each point uses its OWN I.
|
||||
# memory roof from a measured single-thread read: sysbench 37618 MiB/s -> ~19.7 bytes/cycle @ 2 GHz.
|
||||
def getI(v, n=5000):
|
||||
return df[(df.variant==v) & (df.n==n)]["I"].values[0]
|
||||
bw = 37618.01 * 1024**2 / 2e9 # MiB/s -> bytes/cycle at 2 GHz (≈ 19.72)
|
||||
|
||||
for pk,t in [(SCALAR,"scalar (4)"),(PEAK_D,"double (16)"),(PEAK_F,"float (32)")]:
|
||||
ax.axhline(pk, ls="--", color="#999", lw=1.1)
|
||||
ax.text(0.11, pk*1.04, t+" peak", fontsize=9.5, color="#888")
|
||||
|
||||
I5000 = df[(df.variant=="variant_baseline") & (df.n==5000)]["I"].values[0]
|
||||
pts = [
|
||||
("variant_baseline", "#7a7a7a"),
|
||||
("variant_AVX512_1", "#2f9e8f"),
|
||||
("variant_inlining_8_SoA_AVX512_polyexp_moreavx", "#e08a1e"),
|
||||
("variant_float_inlining_SoA_AVX512_polyexp_moreavx", "#c0436a"),
|
||||
("variant_float_inlining_SoA_AVX512_polyexp_moreavx_ilp_2_rsqrt_morersqrt", maroon),
|
||||
# (variant, label, color, group) group "d" = double column, "f" = float column
|
||||
roof = [
|
||||
("variant_baseline", "baseline (scalar)", "#7a7a7a", "d"),
|
||||
("variant_inlining_7_SoA_AVX2", "double, AVX-256", "#9bbbe0", "d"),
|
||||
("variant_AVX512_1", "double, AVX-512 ", "#4f86d6", "d"),
|
||||
("variant_inlining_8_SoA_AVX512_polyexp_moreavx", "double, AVX-512 + polyexp", "#2f5d9e", "d"),
|
||||
("variant_float_inlining_SoA_AVX2_polyexp", "float, AVX-256 + polyexp", "#e58aa6", "f"),
|
||||
("variant_float_inlining_SoA_AVX2_polyexp_rsqrt_loopopt", "float, AVX2 + polyexp + rsqrt", "#d05a82", "f"),
|
||||
("variant_float_inlining_SoA_AVX512_polyexp_moreavx_ilp_2_rsqrt_morersqrt", "float, AVX-512 + rsqrt + ILP (best)", maroon, "f"),
|
||||
]
|
||||
for v,c in pts:
|
||||
ax.plot(I5000, get(v), "o", ms=9, color=c, mec="white", mew=1)
|
||||
LABX = {"d": 2400, "f": 4900} # label right-edge sits just left of each column
|
||||
|
||||
fig, ax = plt.subplots(figsize=(9.5,5.0))
|
||||
xx = np.logspace(-1, 4.2, 200)
|
||||
ax.plot(xx, np.minimum(bw*xx, PEAK_F), color="#bbbbbb", lw=1.4)
|
||||
ax.text(0.115, 0.85, "mem roof: DRAM read ≈ 37.6 GiB/s (≈19.7 B/cycle)", fontsize=8.3, color="#9a9a9a", ha="left")
|
||||
|
||||
ax.text(I5000, 1.3, "n=5000", fontsize=10, color="#555")
|
||||
for pk,t in [(SCALAR,"scalar (4)"), (PEAK_D,"double (16)"), (PEAK_F,"float (32)")]:
|
||||
ax.axhline(pk, ls="--", color="#aaaaaa", lw=1.0)
|
||||
ax.text(0.11, pk*1.05, t+" peak", fontsize=9, color="#888")
|
||||
|
||||
for v,lab,c,grp in roof:
|
||||
x,y = getI(v), get(v)
|
||||
ax.plot(x, y, "o", ms=10, color=c, mec="white", mew=1.4, zorder=5)
|
||||
t = ax.text(LABX[grp], y, lab, color=c, fontsize=8, fontweight="bold", ha="right", va="center", zorder=6)
|
||||
t.set_path_effects([pe.withStroke(linewidth=2.5, foreground="white")])
|
||||
|
||||
ax.text(2812, 0.74, "I ≈ 2.8k", fontsize=8, color="#777", ha="center", va="top")
|
||||
ax.text(5625, 0.74, "I ≈ 5.6k", fontsize=8, color="#777", ha="center", va="top")
|
||||
|
||||
ax.set_xscale("log"); ax.set_yscale("log")
|
||||
ax.set_xlim(0.1, 6500); ax.set_ylim(0.7, 45)
|
||||
ax.set_xlim(0.1, 16000); ax.set_ylim(0.6, 46)
|
||||
ax.set_xlabel("operational intensity I [flops/byte]")
|
||||
ax.set_ylabel("performance [flops/cycle]")
|
||||
#ax.set_title("roofline: deeply compute-bound (I grows with n; work O(n^2), data O(n))", loc="left", fontsize=12)
|
||||
ax.grid(color="#ededed")
|
||||
ax.spines["top"].set_visible(False); ax.spines["right"].set_visible(False)
|
||||
plt.tight_layout()
|
||||
plt.savefig("analysis/fig_roofline.png", dpi=170, bbox_inches="tight")
|
||||
|
||||
|
||||
# plot 4: exp vs the polynomial approx (just to show its basically exact)
|
||||
x = np.linspace(-6, 0, 300)
|
||||
ex = np.exp(x)
|
||||
@@ -205,7 +216,7 @@ plt.savefig("analysis/fig_exp.png", dpi=170, bbox_inches="tight")
|
||||
# "above" or "below" -> alternate the crowded float labels above/below their line.
|
||||
milestones = [
|
||||
("variant_baseline", "baseline", "#7a7a7a", "-", None, "above"),
|
||||
("variant_cutoff_callsite", "baseline + cutoff (best scalar)", "#9e8f2f", "-", None, "below"),
|
||||
#("variant_cutoff_callsite", "baseline + cutoff (best scalar)", "#9e8f2f", "-", None, "below"),
|
||||
("variant_inlining_7_SoA_AVX2", "AVX2 (double)", "#3b6fb0", "--", None, "above"),
|
||||
("variant_inlining_7_SoA_AVX2_polyexp", "AVX2 + polyexp (double)", "#2f9e8f", "--", None, "below"),
|
||||
("variant_inlining_8_SoA_AVX512_polyexp_moreavx", "AVX-512 (double)", "#2f5d9e", "-", None, "below"),
|
||||
@@ -244,4 +255,47 @@ plt.savefig("analysis/fig_milestones.png", dpi=170, bbox_inches="tight")
|
||||
|
||||
|
||||
#plt.show()
|
||||
print("done")
|
||||
print("done")
|
||||
|
||||
# plot 6: wall-clock speedup over baseline (runtime, not performance).
|
||||
# the fair view for optimizations that change the flop count -- cutoff skips ~40% of the
|
||||
# inner loop, symmetry does half the pairs -- so flops/cycle misrepresents them. T_base /
|
||||
# T_variant compares time-to-solve the same n regardless of how many flops each one does.
|
||||
# curated to the milestones that carry the story + symmetry as the cautionary line.
|
||||
basec = df[df.variant == "variant_baseline"].set_index("n")["cycles"].to_dict()
|
||||
def speedline(v):
|
||||
s = df[df.variant == v].sort_values("n")
|
||||
ns = [n for n in s.n.values if n in basec] # baseline has no n=10000 run
|
||||
sp = [basec[n] / s[s.n == n]["cycles"].values[0] for n in ns]
|
||||
return np.array(ns, float), np.array(sp, float)
|
||||
|
||||
# (variant, label, color, linestyle, xpos|None, side)
|
||||
speed = [
|
||||
("variant_cutoff_callsite", "scalar + cutoff (approx)", "#9e8f2f", "-", 260, "above"),
|
||||
("variant_inlining_7_symmetry_2", "symmetry (half the pairs)", "#cf2b2b", "-", 320, "below"),
|
||||
("variant_inlining_7_SoA_AVX2", "double, AVX2", "#9bbbe0", "--", 650, "below"),
|
||||
("variant_inlining_8_SoA_AVX512_polyexp_moreavx", "double, AVX-512 + polyexp", "#2f5d9e", "-", 430, "above"),
|
||||
("variant_float_inlining_SoA_AVX2_polyexp_rsqrt_loopopt", "float, AVX2 + polyexp + rsqrt", "#d05a82", "--", 950, "below"),
|
||||
("variant_float_inlining_SoA_AVX512_polyexp_moreavx_ilp_2_rsqrt_morersqrt", "float, AVX-512 + rsqrt + ILP (best)", maroon, "-", 680, "above"),
|
||||
]
|
||||
fig, ax = plt.subplots(figsize=(9,4.7))
|
||||
ends = []
|
||||
for v,lab,c,ls,xpos,side in speed:
|
||||
x,y = speedline(v)
|
||||
if len(x) == 0:
|
||||
print("skip (not in csv):", v); continue
|
||||
ax.plot(x, y, ls, color=c, marker="o", ms=4, lw=2.4 if c==maroon else 1.8)
|
||||
ends.append((x, y, lab, c, xpos, side))
|
||||
ax.axhline(1, ls=":", color="#999", lw=1.3)
|
||||
ax.text(52, 1.03, "baseline (1x)", fontsize=9, color="#888")
|
||||
ax.set_xscale("log"); ax.set_yscale("log")
|
||||
ax.set_xticks([50,100,200,500,1000,2000,5000]); ax.set_xticklabels(["50","100","200","500","1k","2k","5k"])
|
||||
ax.set_yticks([1,2,5,10,20]); ax.set_yticklabels(["1x","2x","5x","10x","20x"])
|
||||
ax.set_xlim(46,7000); ax.set_ylim(0.8,28)
|
||||
ax.set_xlabel("number of pedestrians n")
|
||||
ax.set_ylabel("speedup over baseline (wall-clock cycles)")
|
||||
ax.grid(color="#e9e9e9")
|
||||
ax.spines["top"].set_visible(False); ax.spines["right"].set_visible(False)
|
||||
plt.tight_layout()
|
||||
label_lines_above(ax, ends, fontsize=7.5, dy_frac=0.02, halo=2)
|
||||
plt.savefig("analysis/fig_speedup.png", dpi=170, bbox_inches="tight")
|
||||