展示HN:1-位盆景,首个商业上可行的1-位LLM
Show HN: 1-Bit Bonsai, the First Commercially Viable 1-Bit LLMs

原始链接: https://prismml.com/

这段代码可视化了“Bonsai 8B”语言模型与可比模型的性能,重点关注基准测试、准确率/大小权衡、吞吐量、能耗和智能密度。 界面包含用于不同可视化的选项卡面板。**基准测试**结果(IFEval、GSM8K等)显示为条形图,用户可以通过下拉菜单选择不同的基准测试。**准确率与大小**以散点图显示,突出显示Bonsai 8B,并可选择帕累托曲线。另一个散点图(**散点图 - 无曲线**)在没有曲线的情况下呈现相同的数据。**吞吐量**和**能耗**使用条形图可视化,比较Bonsai 8B在不同硬件上的表现。最后,**智能密度**以另一个条形图呈现。 交互元素包括悬停提示、用于视觉定制的调色板选择器以及适应移动设备的响应式设计。动画用于逐步显示数据。当选择选项卡或调整窗口大小时,可视化内容会动态更新。

Hacker News 新闻 | 过去 | 评论 | 提问 | 展示 | 工作 | 提交 登录 展示 HN: 1-Bit Bonsai,首个商业上可行的 1-Bit LLM (prismml.com) 19 分,PrismML 2 小时前 | 隐藏 | 过去 | 收藏 | 7 评论 帮助 syntaxing 2 分钟前 | 下一个 [–] 非常有趣,正在我的 Jetson Orin Nano 上构建他们的 llama cpp 分支来测试这个。回复 yodon 23 分钟前 | 上一个 | 下一个 [–] Bonsai 是 1 位还是 1.58 位?回复 woadwarrior01 21 分钟前 | 父评论 | 下一个 [–] 1 位 g128,每个组共享 16 位刻度。所以,实际上是 1.125 位。回复 stogot 14 分钟前 | 上一个 | 下一个 [–] 1 位有什么价值?对于那些不知道的人来说。回复 jacquesm 10 分钟前 | 父评论 | 下一个 [–] 你可以用一条指令处理许多操作。回复 trebligdivad 11 分钟前 | 父评论 | 上一个 | 下一个 [–] 速度和密度。回复 SwellJoe 9 分钟前 | 父评论 | 上一个 | 下一个 [–] 0 或 1 回复 指南 | 常见问题 | 列表 | API | 安全 | 法律 | 申请 YC | 联系方式 搜索:
相关文章

原文
${p.label}`; btn.addEventListener("click", () => applyPalette(key)); container.appendChild(btn); }); })(); document.querySelectorAll(".tab-btn").forEach(btn => { btn.addEventListener("click", () => { document.querySelectorAll(".tab-btn").forEach(b => b.classList.remove("active")); document.querySelectorAll(".tab-panel").forEach(p => p.classList.remove("active")); btn.classList.add("active"); el("panel-" + btn.dataset.tab).classList.add("active"); }); }); const select = el("benchmark-select"); Object.keys(benchmarks).forEach(name => { const opt = document.createElement("option"); opt.value = name; opt.textContent = name; select.appendChild(opt); }); function drawBenchmarkChart(benchmarkName) { const canvas = el("benchmark-canvas"); if (!canvas) return; const dpr = window.devicePixelRatio || 1; const rect = canvas.getBoundingClientRect(); canvas.width = rect.width * dpr; canvas.height = rect.height * dpr; const ctx = canvas.getContext("2d"); ctx.scale(dpr, dpr); const W = rect.width, H = rect.height; const data = benchmarks[benchmarkName]; const sorted = benchmarkModels .map(m => ({ name: m, value: data[m] })) .sort((a, b) => b.value - a.value); const mob = isMobile(); const ml = mob ? 36 : 56, mr = mob ? 8 : 16, mt = mob ? 10 : 16, mb = mob ? 80 : 110; const pw = W - ml - mr, ph = H - mt - mb; const maxVal = 100; const n = sorted.length; const gap = mob ? 2 : 4; const barW = Math.max(6, (pw - gap * (n - 1)) / n); const p = palettes[currentPalette]; ctx.clearRect(0, 0, W, H); ctx.strokeStyle = "rgba(0,0,0,0.08)"; ctx.lineWidth = 1; for (let v = 0; v { const x = ml + i * (barW + gap); const barH = (d.value / maxVal) * ph; const y = mt + ph - barH; const isHighlight = d.name.includes("Bonsai"); if (isHighlight) { const grad = ctx.createLinearGradient(x, y, x, mt + ph); grad.addColorStop(0, p.gradient[0]); grad.addColorStop(1, p.gradient[1]); ctx.fillStyle = grad; } else { ctx.fillStyle = p.benchmark[nonHighlightIdx % p.benchmark.length]; ctx.globalAlpha = 0.75; nonHighlightIdx++; } ctx.fillRect(x, y, barW, barH); ctx.globalAlpha = 1; if (isHighlight && bonsaiImgLight.complete) { const biw = mob ? 24 : 40, bih = mob ? 16 : 28; ctx.drawImage(bonsaiImgLight, x + barW / 2 - biw / 2, y + barH / 2 - bih / 2, biw, bih); } ctx.font = dm(400, mob?8:11); ctx.fillStyle = isHighlight ? p.highlightLabel : "#4c5159"; ctx.textAlign = "center"; ctx.fillText(d.value.toString(), x + barW / 2, y - (mob ? 3 : 5)); ctx.save(); ctx.translate(x + barW / 2, mt + ph + 8); ctx.rotate(-Math.PI / 3); ctx.font = `${isHighlight ? "600" : "400"} ${mob ? 9 : 11}px "Rethink Sans", sans-serif`; ctx.fillStyle = isHighlight ? p.highlightLabel : "#4c5159"; ctx.textAlign = "right"; ctx.fillText(d.name, 0, 0); ctx.restore(); }); } function updateBenchmarkFooter() { const sub = el("benchmark-subtitle"); if (sub) sub.textContent = select.value === "Avg" ? "Average score (IFEval, GSM8K, HumanEval+, BFCL, MuSR, MMLU-Redux)" : "1-bit Bonsai 8B vs. comparable models"; } select.addEventListener("change", () => { drawBenchmarkChart(select.value); updateBenchmarkFooter(); }); function stepBenchmark(dir) { const opts = select.options; select.selectedIndex = (select.selectedIndex + dir + opts.length) % opts.length; drawBenchmarkChart(select.value); updateBenchmarkFooter(); } el("bench-prev").addEventListener("click", () => stepBenchmark(-1)); el("bench-next").addEventListener("click", () => stepBenchmark(1)); drawBenchmarkChart(select.value); updateBenchmarkFooter(); (() => { const leg = el("throughput-legend"); throughputModels.forEach(m => { const item = document.createElement("div"); item.className = "leg-item"; const stripeStyle = m.key === "4-bit 8B" ? "background-image:repeating-linear-gradient(-45deg,#fff 0 1px,transparent 1px 4px);" : ""; item.innerHTML = `` + `${m.key}`; leg.appendChild(item); }); })(); function drawThroughput() { const canvas = el("throughput-canvas"); if (!canvas) return; const dpr = window.devicePixelRatio || 1; const rect = canvas.getBoundingClientRect(); canvas.width = rect.width * dpr; canvas.height = rect.height * dpr; const ctx = canvas.getContext("2d"); ctx.scale(dpr, dpr); const W = rect.width; const H = rect.height; const mob = isMobile(); const ml = mob ? 40 : 64, mr = mob ? 12 : 24, mt = mob ? 10 : 16, mb = mob ? 70 : 84; const pw = W - ml - mr; const ph = H - mt - mb; const maxVal = 400; function yPos(v) { return mt + ph - (v / maxVal) * ph; } ctx.clearRect(0, 0, W, H); ctx.font = dm(400, mob?10:13); ctx.fillStyle = "#4c5159"; ctx.textAlign = "right"; for (let v = 0; v { const gx = ml + gi * groupW; ctx.font = rs(500, mob?10:13); ctx.fillStyle = "#262c35"; ctx.textAlign = "center"; const labelX = gx + groupW / 2; const parts = mob && hw === "iPhone 17 Pro Max" ? ["iPhone 17", "Pro Max"] : hw === "Android (Adreno 830)" ? ["Android", "(Adreno 830)"] : [hw]; parts.forEach((p, pi) => { ctx.fillText(p, labelX, yPos(0) + (mob ? 14 : 20) + pi * (mob ? 12 : 16)); }); const slotCount = throughputModels.length; const slotW = barsW / slotCount; throughputModels.forEach((model, mi) => { const val = throughputData[model.key][gi]; if (val === null) return; const bx = gx + groupPad + mi * slotW + barGap / 2; const bw = slotW - barGap; if (val === null || val === "dnf") { ctx.font = dm(400, mob?8:11); ctx.fillStyle = "#999"; ctx.textAlign = "center"; ctx.fillText("N/A*", bx + bw / 2, yPos(0) - (mob ? 4 : 6)); return; } const barH = (val / maxVal) * ph; const by = yPos(val); if (model.highlight) { const p = palettes[currentPalette]; const grad = ctx.createLinearGradient(bx, by + barH, bx, by); grad.addColorStop(0, p.gradient[1]); grad.addColorStop(1, p.gradient[0]); ctx.fillStyle = grad; } else { ctx.fillStyle = model.color; } ctx.fillRect(bx, by, bw, barH); if (model.key === "4-bit 8B") { ctx.save(); ctx.beginPath(); ctx.rect(bx, by, bw, barH); ctx.clip(); ctx.strokeStyle = "#fff"; ctx.lineWidth = 1; for (let d = -bw; d { const leg = el("energy-legend"); energyModels.forEach(m => { const item = document.createElement("div"); item.className = "leg-item"; item.innerHTML = `` + `${m.key}`; leg.appendChild(item); }); })(); function drawEnergy() { const canvas = el("energy-canvas"); if (!canvas) return; const dpr = window.devicePixelRatio || 1; const rect = canvas.getBoundingClientRect(); canvas.width = rect.width * dpr; canvas.height = rect.height * dpr; const ctx = canvas.getContext("2d"); ctx.scale(dpr, dpr); const W = rect.width; const H = rect.height; const mob = isMobile(); const ml = mob ? 40 : 64, mr = mob ? 12 : 24, mt = mob ? 10 : 16, mb = mob ? 54 : 64; const pw = W - ml - mr; const ph = H - mt - mb; const maxVal = 1.4; function yPos(v) { return mt + ph - (v / maxVal) * ph; } ctx.clearRect(0, 0, W, H); ctx.font = dm(400, mob?10:13); ctx.fillStyle = "#4c5159"; ctx.textAlign = "right"; for (let vi = 0; vi { const gx = ml + gi * groupW; ctx.font = rs(500, mob?10:13); ctx.fillStyle = "#262c35"; ctx.textAlign = "center"; const labelX = gx + groupW / 2; const eParts = mob && hw === "iPhone 17 Pro Max" ? ["iPhone 17", "Pro Max"] : [hw]; eParts.forEach((ep, epi) => { ctx.fillText(ep, labelX, yPos(0) + (mob ? 14 : 20) + epi * (mob ? 12 : 16)); }); energyModels.forEach((model, bi) => { const val = energyData[model.key][gi]; const bx = gx + groupPad + bi * barW + barGap / 2; const bw = barW - barGap; if (val === null || val === "dnf") { if (val === "dnf") { ctx.font = dm(400, mob?8:11); ctx.fillStyle = "#999"; ctx.textAlign = "center"; ctx.fillText("N/A*", bx + bw / 2, yPos(0) - (mob ? 4 : 6)); } return; } const barH = (val / maxVal) * ph; const by = yPos(val); if (model.highlight) { const p = palettes[currentPalette]; const grad = ctx.createLinearGradient(bx, by + barH, bx, by); grad.addColorStop(0, p.gradient[1]); grad.addColorStop(1, p.gradient[0]); ctx.fillStyle = grad; } else { ctx.fillStyle = model.color; } ctx.fillRect(bx, by, bw, barH); const p = palettes[currentPalette]; ctx.font = dm(400, mob?8:11); ctx.fillStyle = model.highlight ? p.highlightLabel : "#999"; ctx.textAlign = "center"; ctx.fillText(val, bx + bw / 2, by - (mob ? 3 : 6)); }); }); const iphoneIdx = energyHardware.indexOf("iPhone 17 Pro Max"); const noteX = ml + iphoneIdx * groupW + groupW / 2; ctx.font = rs(400, mob?10:14); ctx.fillStyle = "#4c5159"; ctx.textAlign = "center"; ctx.fillText("*16-bit 8B doesn't fit on iPhone 17 Pro Max", noteX, H - mb + (mob ? 34 : 48)); } function drawIntelDensity() { const canvas = el("intel-density-canvas"); if (!canvas) return; const dpr = window.devicePixelRatio || 1; const rect = canvas.getBoundingClientRect(); canvas.width = rect.width * dpr; canvas.height = rect.height * dpr; const ctx = canvas.getContext("2d"); ctx.scale(dpr, dpr); const W = rect.width, H = rect.height; const mob = isMobile(); const ml = mob ? 36 : 64, mr = mob ? 8 : 16, mt = mob ? 10 : 16, mb = mob ? 80 : 110; const pw = W - ml - mr, ph = H - mt - mb; const maxVal = 1.2; const data = intelDensityData; const n = data.length; const gap = mob ? 2 : 4; const barW = Math.max(6, (pw - gap * (n - 1)) / n); const p = palettes[currentPalette]; ctx.clearRect(0, 0, W, H); ctx.strokeStyle = "rgba(0,0,0,0.08)"; ctx.lineWidth = 1; for (let v = 0; v { const x = ml + i * (barW + gap); const barH = (d.value / maxVal) * ph; const y = mt + ph - barH; if (d.highlight) { const grad = ctx.createLinearGradient(x, y, x, mt + ph); grad.addColorStop(0, p.gradient[0]); grad.addColorStop(1, p.gradient[1]); ctx.fillStyle = grad; } else { const ci = i - data.filter((dd, ii) => ii { const px = xPos(x); ctx.beginPath(); ctx.moveTo(px, mt); ctx.lineTo(px, H - mb); ctx.stroke(); }); ctx.strokeStyle = "#262c35"; ctx.lineWidth = 1; ctx.beginPath(); ctx.moveTo(ml, H - mb); ctx.lineTo(W - mr, H - mb); ctx.stroke(); ctx.beginPath(); ctx.moveTo(ml, mt); ctx.lineTo(ml, H - mb); ctx.stroke(); ctx.font = dm(400, mob?9:13); ctx.fillStyle = "#4c5159"; ctx.textAlign = "center"; xTicks.forEach(x => { ctx.fillText(x + " GB", xPos(x), H - mb + (mob ? 16 : 24)); }); ctx.textAlign = "right"; for (let y = 40; y 0) { const steps = Math.round(grayCurveP * 100); ctx.save(); ctx.strokeStyle = "rgba(38,44,53,0.35)"; ctx.lineWidth = 2; ctx.setLineDash([6, 4]); ctx.beginPath(); for (let i = 0; i 0) { const endX = bLineS.x + (bLineE.x - bLineS.x) * darkLineP; const endY = bLineS.y + (bLineE.y - bLineS.y) * darkLineP; ctx.save(); ctx.strokeStyle = "#262c35"; ctx.lineWidth = 2; ctx.setLineDash([6, 4]); ctx.beginPath(); ctx.moveTo(bLineS.x, bLineS.y); ctx.lineTo(endX, endY); ctx.stroke(); ctx.setLineDash([]); ctx.restore(); } scatterDots = []; const sorted = [...scatterData].sort((a, b) => (a.highlight ? 1 : 0) - (b.highlight ? 1 : 0)); const newPts = scatterData.filter(d => d.highlight).sort((a, b) => a.size - b.size); const bonsaiXNorm = newPts.map(d => (xPos(d.size) - bLineS.x) / (bLineE.x - bLineS.x)); sorted.forEach(d => { const cx = xPos(d.size); const cy = yPos(d.accuracy); const isHovered = hoveredName === d.name; const r = d.highlight ? 7 : (isHovered ? 7 : 5); scatterDots.push({ name: d.name, cx, cy, r: 5, data: d }); let bAlpha = 1; if (d.highlight) { const bIdx = newPts.findIndex(b => b.name === d.name); const xn = bIdx >= 0 ? bonsaiXNorm[bIdx] : 1; if (darkLineP { if (el("panel-accuracy-size").classList.contains("active")) drawScatter(); if (el("panel-scatter-nocurves").classList.contains("active")) drawScatterNC(); }; const scatterNCData = [ { name: "1-bit Bonsai 8B", size: 1.15, accuracy: 70.5, highlight: true, color: "#12151a" }, { name: "Qwen3 0.6B", size: 1.19, accuracy: 48, color: "#666" }, { name: "Gemma3 1B", size: 2, accuracy: 45.5, color: "#666" }, { name: "LFM2 1.2B", size: 2.34, accuracy: 46.7, color: "#666" }, { name: "Llama 3.2 1B",size: 2.47, accuracy: 39.9, color: "#666" }, { name: "Qwen3 8B", size: 16.38,accuracy: 79.3, color: "#666" }, { name: "Olmo3 7B", size: 14.6, accuracy: 70.9, color: "#666" }, { name: "RNJ 8B", size: 16.62,accuracy: 73.1, color: "#666" }, { name: "Ministral3 8B",size:16.04,accuracy: 71, color: "#666" }, { name: "LFM2 8B", size: 16.68,accuracy: 69.6, color: "#666" }, { name: "Llama 3.1 8B",size: 16.06,accuracy: 67.1, color: "#666" }, { name: "Hermes 3 8B", size: 16.06,accuracy: 65.4, color: "#666" }, { name: "DeepSeek R1-Qwen 7B", size: 15.23, accuracy: 55, color: "#666" }, { name: "Marin 8B", size: 16.06,accuracy: 56.6, color: "#666" } ]; let scatterNCDots = []; let scatterNCAnimPhase = 0; // 0=not started, 1=right, 2=+left, 3=+bonsai (complete) let scatterNCHasAnimated = false; let scatterNCAnimStart = 0; const scatterNCPhaseDuration = 1000; const scatterNCFixedLabels = ["Llama 3.1 8B", "Qwen3 8B", "Ministral3 8B", "Gemma3 1B", "Qwen3 0.6B"]; function scatterNCGroup(d) { if (d.highlight) return "bonsai"; return d.size >= 12 ? "right" : "left"; } function drawScatterNC(hoveredName) { const canvas = el("scatter-nc-canvas"); if (!canvas) return; const dpr = window.devicePixelRatio || 1; const rect = canvas.getBoundingClientRect(); canvas.width = rect.width * dpr; canvas.height = rect.height * dpr; const ctx = canvas.getContext("2d"); ctx.scale(dpr, dpr); const W = rect.width, H = rect.height; const mob = isMobile(); const ml = mob ? 40 : 64, mr = mob ? 16 : 30, mt = mob ? 24 : 36, mb = mob ? 48 : 64; const pw = W - ml - mr, ph = H - mt - mb; const xMin = 0, xMax = 20; const yMin = 20, yMax = 84; function xPos(v) { return ml + ((v - xMin) / (xMax - xMin)) * pw; } function yPos(v) { return mt + ph - ((v - yMin) / (yMax - yMin)) * ph; } ctx.clearRect(0, 0, W, H); ctx.strokeStyle = "rgba(0,0,0,0.08)"; ctx.lineWidth = 1; for (let y = 20; y { const px = xPos(x); ctx.beginPath(); ctx.moveTo(px, mt); ctx.lineTo(px, H - mb); ctx.stroke(); }); ctx.strokeStyle = "#262c35"; ctx.lineWidth = 1; ctx.beginPath(); ctx.moveTo(ml, H - mb); ctx.lineTo(W - mr, H - mb); ctx.stroke(); ctx.beginPath(); ctx.moveTo(ml, mt); ctx.lineTo(ml, H - mb); ctx.stroke(); ctx.font = dm(400, mob?9:13); ctx.fillStyle = "#4c5159"; ctx.textAlign = "center"; xTicks.forEach(x => { ctx.fillText(x + " GB", xPos(x), H - mb + (mob ? 16 : 24)); }); ctx.textAlign = "right"; for (let y = 20; y { const phaseFor = g === "right" ? 1 : g === "left" ? 2 : 3; if (phase > phaseFor) return 1; if (phase scatterNCGroup(d) === "right"); const leftPts = scatterNCData.filter(d => scatterNCGroup(d) === "left"); const rCx = rightPts.reduce((s, d) => s + xPos(d.size), 0) / rightPts.length; const rCy = rightPts.reduce((s, d) => s + yPos(d.accuracy), 0) / rightPts.length; const rRx = Math.max(...rightPts.map(d => Math.abs(xPos(d.size) - rCx))) + 50; const rRy = Math.max(...rightPts.map(d => Math.abs(yPos(d.accuracy) - rCy))) + 50; drawOval(rCx, rCy, rRx, rRy, "7B\u20138B models", phaseAlpha("right")); const lCx = leftPts.reduce((s, d) => s + xPos(d.size), 0) / leftPts.length; const lCy = leftPts.reduce((s, d) => s + yPos(d.accuracy), 0) / leftPts.length; const lRx = Math.max(...leftPts.map(d => Math.abs(xPos(d.size) - lCx))) + 30; const lRy = Math.max(...leftPts.map(d => Math.abs(yPos(d.accuracy) - lCy))) + 30; drawOval(lCx, lCy, lRx, lRy, "0.5\u20131.5B models", phaseAlpha("left")); scatterNCDots = []; const sorted = [...scatterNCData].sort((a, b) => (a.highlight ? 1 : 0) - (b.highlight ? 1 : 0)); sorted.forEach(d => { const cx = xPos(d.size), cy = yPos(d.accuracy); const grp = scatterNCGroup(d); const alpha = phaseAlpha(grp); if (alpha = scatterNCPhaseDuration && scatterNCAnimPhase { const canvas = el("scatter-nc-canvas"); const tooltip = el("scatter-nc-tooltip"); const wrap = el("scatter-nc-wrap"); if (!canvas || !tooltip || !wrap) return; let currentHover = null; canvas.addEventListener("mousemove", e => { const rect = canvas.getBoundingClientRect(); const mx = e.clientX - rect.left, my = e.clientY - rect.top; let closest = null, minD = 20; scatterNCDots.forEach(dot => { const d = Math.hypot(dot.cx - mx, dot.cy - my); if (d { if (currentHover) { currentHover = null; drawScatterNC(null); } tooltip.classList.remove("visible"); }); })(); (() => { const canvas = el("scatter-canvas"); const tooltip = el("scatter-tooltip"); const wrap = el("scatter-wrap"); let currentHover = null; canvas.addEventListener("mousemove", (e) => { const rect = canvas.getBoundingClientRect(); const mx = e.clientX - rect.left; const my = e.clientY - rect.top; let closest = null; let closestDist = Infinity; scatterDots.forEach(dot => { const dx = mx - dot.cx; const dy = my - dot.cy; const dist = Math.sqrt(dx * dx + dy * dy); if (dist Avg: ${closest.data.accuracy}`; tooltip.classList.add("visible"); const wrapRect = wrap.getBoundingClientRect(); let tx = e.clientX - wrapRect.left + 14; let ty = e.clientY - wrapRect.top - 10; if (tx + 160 > wrapRect.width) tx = e.clientX - wrapRect.left - 170; tooltip.style.left = tx + "px"; tooltip.style.top = ty + "px"; } else { canvas.style.cursor = "default"; if (currentHover !== null) { currentHover = null; drawScatter(null); } tooltip.classList.remove("visible"); } }); canvas.addEventListener("mouseleave", () => { canvas.style.cursor = "default"; if (currentHover !== null) { currentHover = null; drawScatter(null); } tooltip.classList.remove("visible"); }); })(); window.addEventListener("resize", () => { if (el("panel-benchmarks").classList.contains("active")) drawBenchmarkChart(select.value); if (el("panel-accuracy-size").classList.contains("active")) drawScatter(); if (el("panel-scatter-nocurves").classList.contains("active")) drawScatterNC(); if (el("panel-intel-density").classList.contains("active")) drawIntelDensity(); if (el("panel-throughput").classList.contains("active")) drawThroughput(); if (el("panel-energy").classList.contains("active")) drawEnergy(); }); document.querySelectorAll(".tab-btn").forEach(btn => { btn.addEventListener("click", () => { const t = btn.dataset.tab; if (t === "benchmarks") raf2(() => drawBenchmarkChart(select.value)); if (t === "accuracy-size") raf2(() => { if (paretoHasAnimated) { scatterAnimProgress = 1; drawScatter(); } else { paretoHasAnimated = true; animateParetoCurves(); } }); if (t === "scatter-nocurves") raf2(() => { if (scatterNCHasAnimated) { scatterNCAnimPhase = 4; drawScatterNC(); } else { scatterNCHasAnimated = true; animateScatterNC(); } }); if (t === "intel-density") raf2(drawIntelDensity); if (t === "throughput") raf2(drawThroughput); if (t === "energy") raf2(drawEnergy); }); }); applyPalette("rose"); const vizEl = el("prism-viz"); const paretoObserver = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting && !paretoHasAnimated) { if (el("panel-accuracy-size").classList.contains("active")) { paretoHasAnimated = true; animateParetoCurves(); } } }); }, { threshold: 1.0 }); paretoObserver.observe(vizEl);
联系我们 contact @ memedata.com