import pandas as pd import numpy as np import matplotlib.pyplot as plt #import seaborn as sns # tried it, the default look was worse df = pd.read_csv("analysis/results.csv") # peaks for the ryzen ai 7 350 (zen5) at 2GHz. 2 fma units and the datapath is 256 bit # -> scalar 4, double 16, float 32 flops/cycle SCALAR = 4 PEAK_D = 16 PEAK_F = 32 maroon = "#A71D5B" def get(v, n=5000): return df[(df.variant==v) & (df.n==n)]["flops_per_cycle"].values[0] def getline(v): s = df[df.variant == v].sort_values("n") return s.n.values, s["flops_per_cycle"].values 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_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_AVX512_polyexp_moreavx_ilp_2_rsqrt_morersqrt", "float, AVX-512 + rsqrt + ILP (best)", maroon, "-"), ] fig, ax = plt.subplots(figsize=(9,4.7)) for v,lab,c,ls in tracks: x,y = getline(v) ax.plot(x, y, ls, color=c, marker="o", ms=4, lw= 2.4 if c==maroon else 1.8, label=lab) for yv,t in [(PEAK_D,"double peak (16)"), (PEAK_F,"float peak (32)")]: ax.axhline(yv, ls=":", color="#999", lw=1.2) ax.text(52, yv*1.03, t, fontsize=9.5, color="#888") ax.set_xscale("log"); ax.set_yscale("log") ax.set_xticks([50,100,200,500,1000,2000,5000,10000]) ax.set_xticklabels(["50","100","200","500","1k","2k","5k","10k"]) ax.set_yticks([1,2,4,8,16,32]); ax.set_yticklabels(["1","2","4","8","16","32"]) ax.set_xlim(46,13000); ax.set_ylim(1,40) ax.set_xlabel("number of pedestrians n") ax.set_ylabel("performance [flops/cycle]") ax.grid(color="#e9e9e9") ax.spines["top"].set_visible(False); ax.spines["right"].set_visible(False) ax.legend(fontsize=9, loc="lower left", framealpha=.95) plt.tight_layout() plt.savefig("analysis/fig_tracks.png", dpi=170, bbox_inches="tight") # plot 2: all te milestones + the symmetry one that got slower bars = [ ("baseline", "variant_baseline"), ("AVX2 (double)", "variant_inlining_7_SoA_AVX2"), ("AVX-512 (double)", "variant_AVX512_1"), ("+ polyexp", "variant_inlining_8_SoA_AVX512_polyexp_moreavx"), ("+ float", "variant_float_inlining_SoA_AVX512_polyexp_moreavx"), ("+ rsqrt & ILP", "variant_float_inlining_SoA_AVX512_polyexp_moreavx_ilp_2_rsqrt_morersqrt"), ("symmetry\n(half the pairs)", "variant_inlining_7_symmetry_2"), ] names = [b[0] for b in bars] spd = [get(b[1])/base for b in bars] cols = ["#7a7a7a","#3b6fb0","#2f9e8f","#e08a1e","#c0436a", maroon, "#cf2b2b"] fig,ax = plt.subplots(figsize=(9,4.7)) yp = np.arange(len(names))[::-1] ax.barh(yp, spd, color=cols, height=0.66) for yy,s in zip(yp,spd): ax.text(s+0.25, yy, str(round(s,1))+"x", va="center", fontsize=11, fontweight="bold", color = "#cf2b2b" if s<1 else "#222") ax.axvline(1, ls="--", color="#888", lw=1) ax.set_yticks(yp); ax.set_yticklabels(names) ax.set_xlim(0,26) ax.set_xlabel("speedup over baseline (n=5000)") #ax.set_title("optimization milestones (and the one that backfired)", loc="left", fontsize=13) ax.grid(axis="x", color="#eee"); ax.grid(axis="y", visible=False) ax.spines["top"].set_visible(False); ax.spines["right"].set_visible(False) 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) 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), ] for v,c in pts: ax.plot(I5000, get(v), "o", ms=9, color=c, mec="white", mew=1) ax.text(I5000, 1.3, "n=5000", fontsize=10, color="#555") ax.set_xscale("log"); ax.set_yscale("log") ax.set_xlim(0.1, 6500); ax.set_ylim(0.7, 45) 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) co = np.polyfit(x, ex, 5) # degree 5 same as in the kernel ap = np.polyval(co, x) fig,ax = plt.subplots(figsize=(4.6,3.7)) ax.plot(x, ex, color="#222", lw=2.4, label="exp(x)") ax.plot(x, ap, "--", color=maroon, lw=2, label="poly approx") ax.set_xlabel("x") ax.set_title("exp -> polynomial", loc="left", fontsize=12) ax.grid(color="#ededed") ax.spines["top"].set_visible(False); ax.spines["right"].set_visible(False) ax.legend(frameon=False, fontsize=10) plt.tight_layout() plt.savefig("analysis/fig_exp.png", dpi=170, bbox_inches="tight") # plot 5: the milestone ladder as lines (this is plot0 + line1, line2, ... for the doc). # one line per relevant optimization step across n, baseline at the bottom. # unlike fig_tracks this includes the scalar steps so you can see where each jump comes from. milestones = [ ("variant_baseline", "baseline", "#7a7a7a", "-"), ("variant_cutoff_callsite", "+ cutoff (best scalar)", "#9e8f2f", "-"), ("variant_inlining_7_SoA_AVX2", "AVX2 (double)", "#3b6fb0", "--"), ("variant_inlining_7_SoA_AVX2_polyexp", "+ polyexp (double)", "#2f9e8f", "--"), ("variant_inlining_8_SoA_AVX512_polyexp_moreavx", "AVX-512 (double)", "#2f5d9e", "-"), ("variant_float_inlining_SoA_AVX2_polyexp", "float AVX2 + polyexp", "#e58aa6", "--"), ("variant_float_inlining_SoA_AVX2_polyexp_rsqrt_loopopt", "float AVX2 + rsqrt", "#e08a1e", "--"), ("variant_float_inlining_SoA_AVX512_polyexp_moreavx", "float AVX-512", "#c0436a", "-"), ("variant_float_inlining_SoA_AVX512_polyexp_moreavx_ilp_2_rsqrt_morersqrt","float AVX-512 + rsqrt + ILP (best)", maroon, "-"), ] fig, ax = plt.subplots(figsize=(9.5,5.2)) for v,lab,c,ls in milestones: x,y = getline(v) if len(x) == 0: # variant not in results.csv -> skip instead of crashing 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.7, label=lab) for yv,t in [(PEAK_D,"double peak (16)"), (PEAK_F,"float peak (32)")]: ax.axhline(yv, ls=":", color="#999", lw=1.2) ax.text(52, yv*1.03, t, fontsize=9.5, color="#888") ax.set_xscale("log"); ax.set_yscale("log") ax.set_xticks([50,100,200,500,1000,2000,5000,10000]) ax.set_xticklabels(["50","100","200","500","1k","2k","5k","10k"]) ax.set_yticks([1,2,4,8,16,32]); ax.set_yticklabels(["1","2","4","8","16","32"]) ax.set_xlim(46,13000); ax.set_ylim(0.8,40) ax.set_xlabel("number of pedestrians n") ax.set_ylabel("performance [flops/cycle]") ax.grid(color="#e9e9e9") ax.spines["top"].set_visible(False); ax.spines["right"].set_visible(False) ax.legend(fontsize=8.5, loc="upper left", ncol=2, framealpha=.95) plt.tight_layout() plt.savefig("analysis/fig_milestones.png", dpi=170, bbox_inches="tight") #plt.show() print("done")