Skip to content

Commit 7ec4013

Browse files
author
Pascal Rheaume
committed
docs: add 3090Ti power cap efficiency plot
Add Python script to generate efficiency charts for RTX 3090Ti using Qwen3.6-27B-UD model.
1 parent 57eb269 commit 7ec4013

1 file changed

Lines changed: 151 additions & 0 deletions

File tree

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
"""Generate 3090Ti power-cap efficiency chart from @pascalrheaume's air-cooled rig.
2+
3+
Source data: 2026-05-14 sweep, single 3090Ti rig (GPU 0 used), air-cooled.
4+
Engine: mainline llama.cpp (ghcr.io/ggml-org/llama.cpp:server-cuda) +
5+
Qwen3.6-27B-UD-Q3_K_XL.gguf, decode-single (10s × 2 timed streams)
6+
7+
Sweep methodology: time-bounded streaming bench (10s/direction at each cap).
8+
Total wall: ~10m 23caps from 230-450W in 10W increments. The time-bounded
9+
approach (vs token-bounded) makes per-cap wall constant ~23s regardless of cap,
10+
so total runtime scales linearly with cap count, not throttle severity.
11+
12+
Sampling fields: GPU temp, SM clock, memory clock, power-throttle %, P-state per cap
13+
(median of in-load samples where util>50%). The extended sweep reveals the full
14+
efficiency curve up to 450W, with peak efficiency at 310W (0.107 TPS/W) and
15+
a gradual decline beyond 330W as the GPU draws more power without proportional
16+
gains in throughput.
17+
"""
18+
import matplotlib.pyplot as plt
19+
20+
# (cap_W, narr_TPS, code_TPS, actual_W, gpu_temp_C, sm_clk_MHz, mem_clk_MHz, pwr_throttle_pct, p_state, eff_TPS_per_W) — 23-cap extended sweep
21+
data = [
22+
(230, 19.88, 20.28, 229.69, 63, 750, 10251, 100, "P2", 0.087),
23+
(240, 22.07, 21.87, 239.54, 64, 825, 10251, 97, "P2", 0.092),
24+
(250, 23.57, 23.17, 249.40, 65, 885, 10251, 100, "P2", 0.095),
25+
(260, 25.17, 25.37, 259.04, 65, 975, 10251, 100, "P2", 0.097),
26+
(270, 26.77, 26.67, 269.08, 66, 1035, 10251, 100, "P2", 0.099),
27+
(280, 28.27, 28.57, 279.07, 67, 1125, 10251, 100, "P2", 0.101),
28+
(290, 30.27, 30.27, 289.19, 67, 1215, 10251, 100, "P2", 0.105),
29+
(300, 31.77, 31.76, 299.83, 68, 1290, 10251, 100, "P2", 0.106),
30+
(310, 33.46, 33.36, 309.38, 69, 1365, 10251, 100, "P2", 0.108),
31+
(320, 34.76, 34.86, 319.43, 69, 1470, 10251, 100, "P2", 0.109),
32+
(330, 36.36, 36.26, 329.20, 70, 1635, 10251, 100, "P2", 0.110),
33+
(340, 37.36, 37.46, 339.45, 70, 1725, 10251, 100, "P2", 0.110),
34+
(350, 38.45, 37.96, 348.79, 71, 1755, 10251, 100, "P2", 0.110), #⭐ sweet spot
35+
(360, 38.96, 38.86, 358.66, 72, 1770, 10251, 97, "P2", 0.109),
36+
(370, 39.25, 39.16, 368.89, 72, 1815, 10251, 97, "P2", 0.106),
37+
(380, 39.46, 39.26, 378.75, 73, 1845, 10251, 100, "P2", 0.104),
38+
(390, 39.86, 39.76, 388.42, 73, 1860, 10251, 100, "P2", 0.103),
39+
(400, 40.06, 39.66, 398.67, 74, 1875, 10251, 100, "P2", 0.100),
40+
(410, 40.06, 40.16, 407.98, 75, 1890, 10251, 97, "P2", 0.098),
41+
(420, 40.35, 39.96, 418.79, 75, 1905, 10251, 97, "P2", 0.096),
42+
(430, 40.45, 40.36, 428.56, 76, 1920, 10251, 97, "P2", 0.094),
43+
(440, 40.55, 40.56, 438.52, 76, 1935, 10251, 100, "P2", 0.092),
44+
(450, 40.45, 40.76, 448.35, 77, 1935, 10251, 100, "P2", 0.090), # stock TDP
45+
]
46+
47+
caps = [d[0] for d in data]
48+
narr = [d[1] for d in data]
49+
code = [d[2] for d in data]
50+
draw = [d[3] for d in data]
51+
gpu_temp = [d[4] for d in data]
52+
sm_clk = [d[5] for d in data]
53+
mem_clk = [d[6] for d in data]
54+
pwr_throttle = [d[7] for d in data]
55+
p_state = [d[8] for d in data]
56+
eff = [d[9] for d in data]
57+
58+
plt.rcParams.update({
59+
"font.family": "sans-serif",
60+
"font.size": 12,
61+
"axes.titlesize": 16,
62+
"axes.titleweight": "bold",
63+
"axes.labelsize": 13,
64+
"figure.facecolor": "white",
65+
"axes.facecolor": "white",
66+
})
67+
68+
fig, ax1 = plt.subplots(figsize=(11, 6.4), dpi=150)
69+
70+
# Left axis: TPS
71+
color_narr = "#1f77b4"
72+
color_code = "#2ca02c"
73+
ax1.plot(caps, narr, "o-", color=color_narr, linewidth=2.2, markersize=6,
74+
label="Narrative TPS", zorder=3)
75+
ax1.plot(caps, code, "s-", color=color_code, linewidth=2.2, markersize=6,
76+
label="Code TPS", zorder=3)
77+
ax1.set_xlabel("Power cap (W)", fontsize=13)
78+
ax1.set_ylabel("Wall TPS (single-stream, llama.cpp mainline)", fontsize=13)
79+
ax1.set_xlim(225, 455)
80+
ax1.set_ylim(14, 43)
81+
ax1.grid(True, alpha=0.3, zorder=0)
82+
ax1.tick_params(axis="both", labelsize=11)
83+
84+
# Right axis: TPS/W efficiency
85+
ax2 = ax1.twinx()
86+
color_eff = "#d62728"
87+
ax2.plot(caps, eff, "^--", color=color_eff, linewidth=1.8, markersize=5,
88+
alpha=0.9, label="Efficiency (narr TPS/W)", zorder=2)
89+
ax2.set_ylabel("Efficiency: TPS/W (narrative)", color=color_eff, fontsize=13)
90+
ax2.tick_params(axis="y", labelcolor=color_eff, labelsize=11)
91+
ax2.set_ylim(0.07, 0.118)
92+
93+
# Sweet spot annotation: 350W
94+
ax1.axvline(350,color="goldenrod", linestyle=":", alpha=0.5, linewidth=1.5)
95+
ax1.annotate(
96+
"★ 350W cap\n0.11 TPS/W (best efficiency)\n38.45 narr / 37.98 code\nSM 1755 MHz, 78% of stock TDP",
97+
xy=(350, 38),
98+
xytext=(320, 28),
99+
fontsize=10.5,
100+
fontweight="bold",
101+
bbox=dict(boxstyle="round,pad=0.4", facecolor="#fff3cd", edgecolor="goldenrod", linewidth=1.2),
102+
arrowprops=dict(arrowstyle="->", color="goldenrod", lw=1.5),
103+
zorder=4,
104+
)
105+
106+
# Stock TDP marker at 450W
107+
ax1.axvline(450, color="#888", linestyle="--", alpha=0.6, linewidth=1.2)
108+
ax1.annotate(
109+
"stock TDP\n450W (GPU 0)",
110+
xy=(450, 40.06),
111+
xytext=(455, 42),
112+
fontsize=10,
113+
ha="left",
114+
color="#555",
115+
fontstyle="italic",
116+
)
117+
118+
# Title
119+
ax1.set_title(
120+
"RTX 3090Ti Qwen3.6-27B + llama.cpp — extended power-cap efficiency curve",
121+
pad=14,
122+
)
123+
124+
# Subtitle
125+
fig.text(
126+
0.5, 0.92,
127+
"1× 3090Ti air-cooled (GPU 0), mainline llama.cpp + Q3_K_XL GGUF, "
128+
"time-bounded single-stream, 230-450W sweep | data: @pascalrheaume (2026-05-14)",
129+
ha="center", fontsize=10, color="#666",
130+
style="italic",
131+
)
132+
133+
# Combined legend
134+
lines1, labels1 = ax1.get_legend_handles_labels()
135+
lines2, labels2 = ax2.get_legend_handles_labels()
136+
ax1.legend(lines1 + lines2, labels1 + labels2,
137+
loc="lower right", fontsize=11, framealpha=0.95,
138+
edgecolor="#ccc")
139+
140+
# Footer
141+
fig.text(
142+
0.99, 0.01,
143+
"github.com/pascalrheaume/club-3090",
144+
ha="right", fontsize=9, color="#888", style="italic",
145+
)
146+
147+
plt.tight_layout(rect=(0, 0.02, 1, 0.92))
148+
149+
out = "/tmp/power_cap_sweep_3090Ti_Qwen36.png"
150+
plt.savefig(out, dpi=150, bbox_inches="tight", facecolor="white")
151+
print(f"Saved: {out}")

0 commit comments

Comments
 (0)