Last active
April 15, 2025 08:35
-
-
Save qinghon/89d3422bee924c2a76b3aaa770815129 to your computer and use it in GitHub Desktop.
rust real-time tracking of the impact of code modification on the final binary on size during development
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html lang="zh"> | |
<head> | |
<meta charset="UTF-8"> | |
<title>Function Size Monitor</title> | |
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script> | |
<script src="https://cdn.jsdelivr.net/npm/[email protected]"></script> | |
<style> | |
.chart-container { | |
margin: 5px 0; | |
padding: inherit; | |
border: 1px solid #ddd; | |
border-radius: 4px; | |
width: 500px; | |
} | |
.chart-title { | |
margin-bottom: 10px; | |
font-family: monospace; | |
} | |
.container { | |
display: flex; | |
flex-wrap: wrap; /* 允许换行 */ | |
gap: 10px; /* 项目之间的间距 */ | |
} | |
.item { | |
flex: 1; /* 项目自动扩展填充空间 */ | |
/*min-width: 200px; !* 设置最小宽度,决定每行显示多少个 *!*/ | |
/* 其他样式... */ | |
} | |
</style> | |
</head> | |
<body> | |
<div id="chart-list" class="container"></div> | |
<script> | |
const MAX_HISTORY = 10; | |
let currentHash = ''; | |
// 数据获取与存储 | |
async function checkUpdate() { | |
try { | |
const res = await fetch(`size_data.json?_=${Date.now()}`); | |
const newData = await res.json(); | |
const ts = newData['timestamp']; | |
newData['timestamp'] = ''; | |
const newHash = await digestMessage(JSON.stringify(newData)); | |
newData['timestamp'] = ts; | |
if (newHash !== currentHash) { | |
currentHash = newHash; | |
storeHistory(newData); | |
renderCharts(); | |
} | |
} catch (err) { | |
console.error('Error:', err); | |
} | |
} | |
// SHA-256哈希生成 | |
async function digestMessage(message) { | |
const encoder = new TextEncoder(); | |
const data = encoder.encode(message); | |
const hashBuffer = await crypto.subtle.digest('SHA-1', data); | |
return Array.from(new Uint8Array(hashBuffer)) | |
.map(b => b.toString(16).padStart(2, '0')).join(''); | |
} | |
// 历史数据存储 | |
function storeHistory(data) { | |
const history = JSON.parse(localStorage.getItem('sizeHistory') || '[]'); | |
history.push({ | |
timestamp: data.timestamp, | |
fileSize: data['file-size'], | |
textSectionSize: data['text-section-size'], | |
functions: data.functions.reduce((acc, f) => { | |
acc[f.name] = f.size; | |
return acc; | |
}, {}) | |
}); | |
if (history.length > MAX_HISTORY) { | |
history.splice(0, history.length - MAX_HISTORY); | |
} | |
localStorage.setItem('sizeHistory', JSON.stringify(history)); | |
} | |
// 图表渲染 | |
function renderCharts() { | |
const history = JSON.parse(localStorage.getItem('sizeHistory') || '[]'); | |
// 清空容器 | |
const container = document.getElementById('chart-list'); | |
container.innerHTML = ''; | |
if (history.length < 2) { | |
document.getElementById('chart-list').innerHTML = '<p>需要至少两次构建数据才能显示变化</p>'; | |
return; | |
} | |
// 渲染总大小图表 | |
if (history.length >= 1) { | |
const totalWrapper = document.createElement('div'); | |
totalWrapper.className = 'chart-container'; | |
totalWrapper.innerHTML = ` | |
<h3 class="chart-title">总文件大小变化</h3> | |
<canvas id="chart-total"></canvas> | |
`; | |
container.appendChild(totalWrapper); | |
renderChart( | |
document.getElementById('chart-total'), | |
history.map(h => h.timestamp), | |
history.map(h => h.fileSize), | |
'Total File Size (bytes)', | |
['#2196F3', '#FF9800'] // 添加第二个数据集颜色 | |
); | |
const textWrapper = document.createElement('div'); | |
textWrapper.className = 'chart-container'; | |
textWrapper.innerHTML = ` | |
<h3 class="chart-title">text大小变化</h3> | |
<canvas id="chart-text"></canvas> | |
`; | |
container.appendChild(textWrapper); | |
renderChart( | |
document.getElementById('chart-text'), | |
history.map(h => h.timestamp), | |
history.map(h => h.textSectionSize), | |
'Text Size (bytes)', | |
['#2196F3', '#FF9800'] // 添加第二个数据集颜色 | |
); | |
} | |
// 计算变化量 | |
const changes = calculateChanges(history); | |
const sortedFunctions = Object.keys(changes) | |
.filter(funcName => hasChanges(funcName, history)) | |
.sort((a, b) => Math.abs(changes[b]) - Math.abs(changes[a])); | |
// 为每个函数创建图表 | |
sortedFunctions.forEach(funcName => { | |
const chartId = `chart-${funcName.replace(/[^a-z0-9]/gi, '_')}`; | |
const wrapper = document.createElement('div'); | |
wrapper.className = 'chart-container'; | |
wrapper.innerHTML = ` | |
<h3 class="chart-title">${funcName}</h3> | |
<canvas id="${chartId}"></canvas> | |
`; | |
container.appendChild(wrapper); | |
renderChart( | |
document.getElementById(chartId), | |
history.map(h => h.timestamp), | |
history.map(h => h.functions[funcName] || 0), | |
`Size Changes - ${funcName}` | |
); | |
}); | |
} | |
// 检查函数在历史记录中是否有变化 | |
function hasChanges(funcName, history) { | |
for (let i = 1; i < history.length; i++) { | |
const prev = history[i - 1].functions[funcName] || 0; | |
const curr = history[i].functions[funcName] || 0; | |
if (prev !== curr) return true; | |
} | |
return false; | |
} | |
// 计算函数变化量 | |
function calculateChanges(history) { | |
const latest = history[history.length - 1].functions; | |
const prev = history[history.length - 2].functions; | |
return Object.keys(latest).reduce((acc, name) => { | |
acc[name] = latest[name] - (prev[name] || 0); | |
return acc; | |
}, {}); | |
} | |
// 单个图表渲染逻辑 | |
function renderChart(canvas, labels, data, title, colors = ['#4CAF50']) { | |
const datasets = Array.isArray(data[0]) ? | |
data.map((d, i) => ({ | |
label: title + ' ' + (i + 1), | |
data: d, | |
borderColor: colors[i % colors.length], | |
})) : [{ | |
label: title, | |
data, | |
borderColor: colors[0], | |
datalabels: { | |
display: true, | |
} | |
}]; | |
new Chart(canvas, { | |
type: 'line', | |
data: { | |
labels, | |
datasets | |
}, | |
options: { | |
transitions: { | |
show: { | |
animations: { | |
x: {from: 0 }, | |
y: {from: 0 } | |
} | |
}, | |
hide: { | |
animations: { | |
x: {to: 0}, | |
y: {to: 0 } | |
} | |
} | |
}, | |
responsive: true, | |
scales: { | |
y: { | |
beginAtZero: false, | |
title: {display: true, text: 'B'} | |
} | |
}, | |
plugins: { | |
title: {display: true, text: title}, | |
datalabels: { | |
display: true, | |
formatter: (value) => { | |
return value | |
}, | |
} | |
} | |
} | |
}); | |
} | |
// 每2秒检查更新 | |
setInterval(checkUpdate, 2000); | |
checkUpdate(); | |
</script> | |
</body> | |
</html> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
mkdir -p build | |
python3 -m http.server --directory build & | |
export RUSTFLAGS="-Zlocation-detail=none -Zfmt-debug=none -Cstrip=none" | |
cargo watch --ignore "${BUILD_DIR}" -x check \ | |
-s 'echo "{\"timestamp\": \"$(date -u +"%H:%M:%SZ")\"}" > build/metadata.json' \ | |
-s 'cargo +nightly bloat -Z build-std=std,panic_abort -Z build-std-features="optimize_for_size" -Z build-std-features=panic_immediate_abort -Z unstable-options --release --message-format=json -n 0 | jq -s add build/metadata.json - > build/size_data.json' \ | |
-s 'rm build/metadata.json' |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment