paper-burner/tests/performance/test-katex-cache.html

437 lines
13 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>KaTeX 缓存性能测试 - Phase 4.2+</title>
<link rel="stylesheet" href="https://gcore.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.css">
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
background: #f5f5f5;
padding: 20px;
}
.container {
max-width: 1200px;
margin: 0 auto;
}
.header {
background: white;
padding: 24px;
border-radius: 8px;
margin-bottom: 20px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
h1 { margin-bottom: 12px; color: #333; }
.warning-box {
background: #fff3cd;
border: 2px solid #ffc107;
border-radius: 6px;
padding: 16px;
margin: 16px 0;
}
.success-box {
background: #d4edda;
border: 2px solid #28a745;
border-radius: 6px;
padding: 16px;
margin: 16px 0;
}
.success-box h3 {
color: #155724;
margin-bottom: 8px;
}
.controls {
display: flex;
gap: 12px;
margin: 16px 0;
flex-wrap: wrap;
}
button {
padding: 10px 20px;
border: none;
border-radius: 6px;
cursor: pointer;
font-size: 14px;
font-weight: 500;
transition: all 0.2s;
}
.btn-primary { background: #4CAF50; color: white; }
.btn-primary:hover { background: #45a049; }
.btn-secondary { background: #2196F3; color: white; }
.btn-secondary:hover { background: #0b7dda; }
.comparison {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20px;
margin-bottom: 20px;
}
.test-card {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
.test-card h3 {
margin-bottom: 12px;
color: #333;
}
.metric {
display: flex;
justify-content: space-between;
padding: 10px 12px;
margin: 8px 0;
background: #f8f9fa;
border-radius: 4px;
font-size: 14px;
}
.metric.highlight {
background: #e8f5e9;
border-left: 4px solid #4CAF50;
font-weight: 600;
}
.metric.warning {
background: #ffebee;
border-left: 4px solid #f44336;
}
.huge-improvement {
font-size: 32px;
font-weight: bold;
color: #2e7d32;
text-align: center;
margin: 20px 0;
}
.formulas-preview {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
margin-bottom: 20px;
max-height: 400px;
overflow-y: auto;
}
.console-log {
background: #1e1e1e;
color: #d4d4d4;
padding: 16px;
border-radius: 4px;
font-family: 'Courier New', monospace;
font-size: 13px;
line-height: 1.6;
max-height: 300px;
overflow-y: auto;
white-space: pre-wrap;
}
.console-log .success { color: #81c784; }
.console-log .warning { color: #ffb74d; }
.console-log .info { color: #4fc3f7; }
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>🚀 Phase 4.2+ KaTeX 公式缓存性能测试</h1>
<p>测试缓存对公式渲染性能的影响(针对 4.6s 阻塞问题)</p>
<div class="warning-box">
<h3>⚠️ 测试场景</h3>
<p><strong>问题:</strong> 打开充满公式的 chatbot 时KaTeX 渲染阻塞主线程 4.6 秒</p>
<p><strong>优化:</strong> 使用 LRU 缓存避免重复公式的重新渲染</p>
<p><strong>预期收益:</strong> 缓存命中时渲染时间减少 99%(从 50ms 降至 0.5ms</p>
</div>
<div class="controls">
<button class="btn-primary" onclick="runTest(50)">测试 50 个公式</button>
<button class="btn-primary" onclick="runTest(100)">测试 100 个公式</button>
<button class="btn-secondary" onclick="viewCacheStats()">查看缓存统计</button>
<button class="btn-secondary" onclick="clearCache()">清空缓存</button>
</div>
</div>
<div class="comparison" id="results-area"></div>
<div class="formulas-preview">
<h2>📝 测试公式预览</h2>
<div id="formulas-preview"></div>
</div>
<div class="test-card">
<h2>📝 测试日志</h2>
<div id="console-log" class="console-log">等待测试...</div>
</div>
</div>
<!-- 加载依赖 -->
<script src="https://gcore.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.js"></script>
<script src="../../js/chatbot/utils/katex-cache.js"></script>
<script>
// 测试公式集合
const TEST_FORMULAS = [
'E = mc^2',
'\\int_{0}^{\\infty} e^{-x^2} dx = \\frac{\\sqrt{\\pi}}{2}',
'\\sum_{n=1}^{\\infty} \\frac{1}{n^2} = \\frac{\\pi^2}{6}',
'x = \\frac{-b \\pm \\sqrt{b^2 - 4ac}}{2a}',
'f(x) = \\frac{1}{\\sigma\\sqrt{2\\pi}} e^{-\\frac{(x-\\mu)^2}{2\\sigma^2}}',
'\\nabla \\times \\vec{E} = -\\frac{\\partial \\vec{B}}{\\partial t}',
'\\lim_{n \\to \\infty} \\left(1 + \\frac{1}{n}\\right)^n = e',
'\\begin{pmatrix} a & b \\\\ c & d \\end{pmatrix} \\begin{pmatrix} x \\\\ y \\end{pmatrix} = \\begin{pmatrix} ax+by \\\\ cx+dy \\end{pmatrix}',
'P(A|B) = \\frac{P(B|A)P(A)}{P(B)}',
'\\oint_{\\partial S} \\vec{E} \\cdot d\\vec{l} = -\\frac{d}{dt}\\int_S \\vec{B} \\cdot d\\vec{A}'
];
function log(message, type = 'info') {
const logEl = document.getElementById('console-log');
const timestamp = new Date().toLocaleTimeString();
logEl.innerHTML += `<span class="${type}">[${timestamp}] ${message}</span>\n`;
logEl.scrollTop = logEl.scrollHeight;
console.log(`[${type.toUpperCase()}] ${message}`);
}
// 测试无缓存渲染
async function testWithoutCache(formulas, rounds = 3) {
log('开始测试:无缓存渲染', 'info');
const times = [];
for (let round = 0; round < rounds; round++) {
const roundStart = performance.now();
formulas.forEach(tex => {
katex.renderToString(tex, {
displayMode: true,
output: 'html',
strict: 'ignore',
throwOnError: false
});
});
times.push(performance.now() - roundStart);
}
const avgTime = times.reduce((a, b) => a + b, 0) / times.length;
log(`无缓存完成: 平均 ${avgTime.toFixed(0)} ms`, 'success');
return {
avgTime,
minTime: Math.min(...times),
maxTime: Math.max(...times),
totalRenders: formulas.length * rounds
};
}
// 测试缓存渲染
async function testWithCache(formulas, rounds = 3) {
log('开始测试:缓存渲染', 'info');
// 第一轮:缓存未命中
const firstRoundStart = performance.now();
formulas.forEach(tex => {
window.renderKatexCached(tex, {
displayMode: true,
output: 'html',
strict: 'ignore',
throwOnError: false
});
});
const firstRoundTime = performance.now() - firstRoundStart;
log(`第一轮(缓存未命中): ${firstRoundTime.toFixed(0)} ms`, 'info');
// 后续轮次:缓存命中
const times = [];
for (let round = 0; round < rounds - 1; round++) {
const roundStart = performance.now();
formulas.forEach(tex => {
window.renderKatexCached(tex, {
displayMode: true,
output: 'html',
strict: 'ignore',
throwOnError: false
});
});
times.push(performance.now() - roundStart);
}
const avgCachedTime = times.reduce((a, b) => a + b, 0) / times.length;
log(`后续轮次(缓存命中): 平均 ${avgCachedTime.toFixed(2)} ms`, 'success');
return {
firstRoundTime,
avgCachedTime,
minTime: Math.min(...times),
maxTime: Math.max(...times),
totalRenders: formulas.length * rounds
};
}
// 运行测试
async function runTest(formulaCount) {
log('========================================', 'info');
log(`开始测试:${formulaCount} 个公式,每个渲染 3 次`, 'info');
log('========================================', 'info');
// 准备测试公式(重复使用基础公式)
const formulas = [];
for (let i = 0; i < formulaCount; i++) {
formulas.push(TEST_FORMULAS[i % TEST_FORMULAS.length]);
}
// 清空缓存
window.katexCache.clear();
// 测试无缓存
const noCacheResult = await testWithoutCache(formulas, 3);
// 等待一下
await new Promise(r => setTimeout(r, 500));
// 测试缓存
const cacheResult = await testWithCache(formulas, 3);
// 显示结果
displayResults(noCacheResult, cacheResult, formulaCount);
// 显示公式预览
displayFormulas(formulas.slice(0, 10));
log('========================================', 'success');
log('测试完成!', 'success');
log('========================================', 'success');
}
// 显示结果
function displayResults(noCacheResult, cacheResult, formulaCount) {
const improvement = ((noCacheResult.avgTime - cacheResult.avgCachedTime) / noCacheResult.avgTime * 100);
const html = `
<div class="test-card">
<h3>⏱️ 无缓存渲染</h3>
<div class="metric warning">
<span>平均耗时:</span>
<span><strong>${noCacheResult.avgTime.toFixed(0)} ms</strong></span>
</div>
<div class="metric">
<span>最小耗时:</span>
<span>${noCacheResult.minTime.toFixed(0)} ms</span>
</div>
<div class="metric">
<span>最大耗时:</span>
<span>${noCacheResult.maxTime.toFixed(0)} ms</span>
</div>
<div class="metric">
<span>总渲染数:</span>
<span>${noCacheResult.totalRenders} 次</span>
</div>
</div>
<div class="test-card">
<h3>⚡ 缓存渲染</h3>
<div class="metric highlight">
<span>缓存命中耗时:</span>
<span><strong>${cacheResult.avgCachedTime.toFixed(2)} ms</strong></span>
</div>
<div class="metric">
<span>首次未命中:</span>
<span>${cacheResult.firstRoundTime.toFixed(0)} ms</span>
</div>
<div class="metric highlight">
<span>性能提升:</span>
<span><strong>↑ ${improvement.toFixed(1)}%</strong></span>
</div>
<div class="metric highlight">
<span>速度倍数:</span>
<span><strong>${(noCacheResult.avgTime / cacheResult.avgCachedTime).toFixed(0)}x 更快</strong></span>
</div>
<div class="huge-improvement">
🚀 ${improvement.toFixed(0)}% 性能提升!
</div>
<div class="success-box">
<h3>✅ 实际应用场景</h3>
<p>如果你的 chatbot 有 ${formulaCount} 个公式:</p>
<ul style="margin: 8px 0 0 20px; line-height: 1.8;">
<li><strong>无缓存:</strong> 每次打开需要 ${noCacheResult.avgTime.toFixed(0)} ms</li>
<li><strong>有缓存:</strong> 第二次打开只需 <strong>${cacheResult.avgCachedTime.toFixed(2)} ms</strong></li>
<li><strong>节省时间:</strong> <strong>${(noCacheResult.avgTime - cacheResult.avgCachedTime).toFixed(0)} ms</strong></li>
</ul>
</div>
</div>
`;
document.getElementById('results-area').innerHTML = html;
}
// 显示公式预览
function displayFormulas(formulas) {
const preview = document.getElementById('formulas-preview');
preview.innerHTML = '<p style="margin-bottom: 12px; color: #666;">测试公式示例(仅显示前 10 个):</p>';
formulas.forEach((tex, index) => {
const div = document.createElement('div');
div.style.margin = '12px 0';
div.style.padding = '12px';
div.style.background = '#f8f9fa';
div.style.borderRadius = '4px';
try {
const html = window.renderKatexCached(tex, {
displayMode: true,
output: 'html',
strict: 'ignore',
throwOnError: false
});
div.innerHTML = `<div style="color: #666; font-size: 12px; margin-bottom: 4px;">公式 ${index + 1}</div>${html}`;
} catch (e) {
div.textContent = `公式 ${index + 1}: 渲染失败`;
}
preview.appendChild(div);
});
}
// 查看缓存统计
function viewCacheStats() {
window.getKatexCacheStats();
}
// 清空缓存
function clearCache() {
window.katexCache.clear();
log('缓存已清空', 'warning');
alert('缓存已清空!');
}
// 页面加载完成
window.addEventListener('load', function() {
log('页面加载完成', 'success');
log('KaTeX 缓存系统已就绪', 'info');
log('点击上方按钮开始测试', 'info');
});
</script>
</body>
</html>