paper-burner/tests/performance/test-markdown-worker.html

572 lines
17 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>Markdown Worker 性能测试 - Phase 5.1.2</title>
<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;
}
.info-box {
background: #e3f2fd;
border: 1px solid #2196F3;
border-radius: 4px;
padding: 16px;
margin: 16px 0;
}
.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;
}
.btn-warning {
background: #ff9800;
color: white;
}
.btn-warning:hover {
background: #e68900;
}
.test-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20px;
margin-bottom: 20px;
}
.test-panel {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
.test-panel h2 {
font-size: 18px;
margin-bottom: 12px;
color: #333;
}
.metric {
display: flex;
justify-content: space-between;
padding: 10px;
margin: 6px 0;
background: #f8f9fa;
border-radius: 4px;
}
.metric.highlight {
background: #e8f5e9;
border-left: 4px solid #4CAF50;
font-weight: 600;
}
.progress {
margin: 12px 0;
}
.progress-bar {
height: 6px;
background: #e0e0e0;
border-radius: 3px;
overflow: hidden;
}
.progress-fill {
height: 100%;
background: #4CAF50;
transition: width 0.3s;
}
.progress-text {
font-size: 13px;
color: #666;
margin-top: 4px;
}
.results {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
margin-bottom: 20px;
}
.comparison-table {
width: 100%;
border-collapse: collapse;
margin-top: 16px;
}
.comparison-table th,
.comparison-table td {
padding: 12px;
text-align: left;
border: 1px solid #e0e0e0;
}
.comparison-table th {
background: #f5f5f5;
font-weight: 600;
}
.winner {
background: #e8f5e9 !important;
font-weight: bold;
color: #2e7d32;
}
.console-output {
background: #1e1e1e;
color: #d4d4d4;
padding: 16px;
border-radius: 4px;
font-family: 'Courier New', monospace;
font-size: 13px;
line-height: 1.6;
max-height: 400px;
overflow-y: auto;
white-space: pre-wrap;
}
.console-output .info { color: #4fc3f7; }
.console-output .success { color: #81c784; }
.console-output .warning { color: #ffb74d; }
.console-output .error { color: #e57373; }
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>🚀 Phase 5.1.2 - Markdown Worker 性能测试</h1>
<p>对比 Web Worker 多线程解析 vs. 主线程解析的性能差异</p>
<div class="info-box">
<strong>测试说明:</strong>
<ul style="margin: 8px 0 0 20px;">
<li>测试不同长度的 Markdown 文本解析性能</li>
<li>对比 Worker 池和主线程的性能指标</li>
<li>验证流式更新场景的流畅度</li>
<li>查看控制台获取详细日志</li>
</ul>
</div>
<div class="controls">
<button class="btn-primary" onclick="runAllTests()">🔬 运行所有测试</button>
<button class="btn-secondary" onclick="runShortTextTest()">测试短文本 (500字)</button>
<button class="btn-secondary" onclick="runMediumTextTest()">测试中文本 (5000字)</button>
<button class="btn-secondary" onclick="runLongTextTest()">测试长文本 (20000字)</button>
<button class="btn-warning" onclick="viewStats()">📊 查看统计</button>
</div>
</div>
<div class="test-grid">
<div class="test-panel">
<h2>🔵 Web Worker 模式</h2>
<div id="worker-status" class="metric">状态:等待测试</div>
<div class="progress">
<div class="progress-bar">
<div class="progress-fill" id="worker-progress" style="width: 0%"></div>
</div>
<div class="progress-text" id="worker-progress-text">...</div>
</div>
<div id="worker-metrics"></div>
</div>
<div class="test-panel">
<h2>🟠 主线程模式</h2>
<div id="main-status" class="metric">状态:等待测试</div>
<div class="progress">
<div class="progress-bar">
<div class="progress-fill" id="main-progress" style="width: 0%"></div>
</div>
<div class="progress-text" id="main-progress-text">...</div>
</div>
<div id="main-metrics"></div>
</div>
</div>
<div class="results">
<h2>📊 对比结果</h2>
<div id="comparison-results">
<p style="color: #666;">运行测试后将显示对比结果...</p>
</div>
</div>
<div class="results">
<h2>📝 测试日志</h2>
<div id="console-log" class="console-output">
等待测试...
</div>
</div>
</div>
<!-- 加载依赖 -->
<script src="https://gcore.jsdelivr.net/npm/marked/marked.min.js"></script>
<script src="../../js/chatbot/config/performance-config.js"></script>
<script src="../../js/chatbot/utils/performance-monitor.js"></script>
<script src="../../js/workers/markdown-worker-pool.js"></script>
<script>
// 测试数据生成
function generateMarkdown(length) {
const paragraphs = Math.ceil(length / 100);
let md = '# 性能测试文档\n\n';
for (let i = 0; i < paragraphs; i++) {
md += `## 章节 ${i + 1}\n\n`;
md += `这是第 ${i + 1} 个段落。`.repeat(5) + '\n\n';
if (i % 3 === 0) {
md += '```javascript\n';
md += 'function test() {\n';
md += ' return "Hello World";\n';
md += '}\n';
md += '```\n\n';
}
if (i % 5 === 0) {
md += '- 列表项 1\n';
md += '- 列表项 2\n';
md += '- 列表项 3\n\n';
}
}
return md;
}
// 测试结果存储
const testResults = {
worker: [],
mainThread: []
};
// 日志函数
function log(message, type = 'info') {
const logEl = document.getElementById('console-log');
const timestamp = new Date().toLocaleTimeString();
const className = type;
logEl.innerHTML += `<span class="${className}">[${timestamp}] ${message}</span>\n`;
logEl.scrollTop = logEl.scrollHeight;
console.log(`[${type.toUpperCase()}] ${message}`);
}
// 更新进度
function updateProgress(mode, percent, text) {
document.getElementById(`${mode}-progress`).style.width = percent + '%';
document.getElementById(`${mode}-progress-text`).textContent = text;
}
// 更新状态
function updateStatus(mode, text) {
document.getElementById(`${mode}-status`).textContent = text;
}
// 更新指标
function updateMetrics(mode, metrics) {
const metricsEl = document.getElementById(`${mode}-metrics`);
metricsEl.innerHTML = `
<div class="metric"><span>平均耗时:</span><span>${metrics.avgTime.toFixed(2)} ms</span></div>
<div class="metric"><span>最小耗时:</span><span>${metrics.minTime.toFixed(2)} ms</span></div>
<div class="metric"><span>最大耗时:</span><span>${metrics.maxTime.toFixed(2)} ms</span></div>
<div class="metric"><span>总耗时:</span><span>${metrics.totalTime.toFixed(2)} ms</span></div>
`;
}
// 测试 Worker 模式
async function testWorkerMode(markdown, rounds = 10) {
log('开始测试 Worker 模式...', 'info');
updateStatus('worker', '状态:测试中...');
const times = [];
for (let i = 0; i < rounds; i++) {
const start = performance.now();
try {
await window.markdownWorkerPool.parse(markdown);
const duration = performance.now() - start;
times.push(duration);
updateProgress('worker', ((i + 1) / rounds) * 100, `${i + 1}/${rounds}`);
} catch (error) {
log(`Worker 测试失败: ${error.message}`, 'error');
times.push(NaN);
}
}
const validTimes = times.filter(t => !isNaN(t));
const metrics = {
avgTime: validTimes.reduce((a, b) => a + b, 0) / validTimes.length,
minTime: Math.min(...validTimes),
maxTime: Math.max(...validTimes),
totalTime: validTimes.reduce((a, b) => a + b, 0)
};
updateStatus('worker', '状态:完成 ✅');
updateMetrics('worker', metrics);
log(`Worker 模式完成: 平均 ${metrics.avgTime.toFixed(2)} ms`, 'success');
return metrics;
}
// 测试主线程模式
async function testMainThreadMode(markdown, rounds = 10) {
log('开始测试主线程模式...', 'info');
updateStatus('main', '状态:测试中...');
const times = [];
for (let i = 0; i < rounds; i++) {
const start = performance.now();
try {
marked.parse(markdown);
const duration = performance.now() - start;
times.push(duration);
updateProgress('main', ((i + 1) / rounds) * 100, `${i + 1}/${rounds}`);
} catch (error) {
log(`主线程测试失败: ${error.message}`, 'error');
times.push(NaN);
}
}
const validTimes = times.filter(t => !isNaN(t));
const metrics = {
avgTime: validTimes.reduce((a, b) => a + b, 0) / validTimes.length,
minTime: Math.min(...validTimes),
maxTime: Math.max(...validTimes),
totalTime: validTimes.reduce((a, b) => a + b, 0)
};
updateStatus('main', '状态:完成 ✅');
updateMetrics('main', metrics);
log(`主线程模式完成: 平均 ${metrics.avgTime.toFixed(2)} ms`, 'success');
return metrics;
}
// 显示对比结果
function displayComparison(workerMetrics, mainMetrics, testName) {
const improvement = ((mainMetrics.avgTime - workerMetrics.avgTime) / mainMetrics.avgTime * 100);
const faster = workerMetrics.avgTime < mainMetrics.avgTime ? 'worker' : 'main';
const html = `
<h3>${testName} - 性能对比</h3>
<table class="comparison-table">
<thead>
<tr>
<th>指标</th>
<th>Web Worker</th>
<th>主线程</th>
<th>改善</th>
</tr>
</thead>
<tbody>
<tr>
<td>平均耗时</td>
<td class="${faster === 'worker' ? 'winner' : ''}">${workerMetrics.avgTime.toFixed(2)} ms</td>
<td class="${faster === 'main' ? 'winner' : ''}">${mainMetrics.avgTime.toFixed(2)} ms</td>
<td>${improvement > 0 ? '↓' : '↑'} ${Math.abs(improvement).toFixed(1)}%</td>
</tr>
<tr>
<td>最小耗时</td>
<td>${workerMetrics.minTime.toFixed(2)} ms</td>
<td>${mainMetrics.minTime.toFixed(2)} ms</td>
<td>-</td>
</tr>
<tr>
<td>最大耗时</td>
<td>${workerMetrics.maxTime.toFixed(2)} ms</td>
<td>${mainMetrics.maxTime.toFixed(2)} ms</td>
<td>-</td>
</tr>
<tr>
<td>总耗时</td>
<td>${workerMetrics.totalTime.toFixed(0)} ms</td>
<td>${mainMetrics.totalTime.toFixed(0)} ms</td>
<td>${improvement > 0 ? '↓' : '↑'} ${Math.abs(improvement).toFixed(1)}%</td>
</tr>
</tbody>
</table>
<div class="info-box" style="margin-top: 16px; background: ${improvement > 0 ? '#e8f5e9' : '#fff3cd'};">
<strong>结论:</strong>
${improvement > 0
? `✅ <strong>Web Worker 性能提升 ${improvement.toFixed(1)}%</strong>,主线程阻塞时间显著减少`
: `⚠️ 对于短文本通信开销可能超过计算开销Worker 性能反而稍差`
}
</div>
`;
document.getElementById('comparison-results').innerHTML = html;
}
// 单独测试函数
async function runShortTextTest() {
document.getElementById('console-log').innerHTML = '';
log('========================================', 'info');
log('开始测试:短文本 (500字)', 'info');
log('========================================', 'info');
const markdown = generateMarkdown(500);
log(`生成了 ${markdown.length} 字符的测试文本`, 'info');
const workerMetrics = await testWorkerMode(markdown, 20);
await new Promise(resolve => setTimeout(resolve, 500));
const mainMetrics = await testMainThreadMode(markdown, 20);
displayComparison(workerMetrics, mainMetrics, '短文本测试');
}
async function runMediumTextTest() {
document.getElementById('console-log').innerHTML = '';
log('========================================', 'info');
log('开始测试:中文本 (5000字)', 'info');
log('========================================', 'info');
const markdown = generateMarkdown(5000);
log(`生成了 ${markdown.length} 字符的测试文本`, 'info');
const workerMetrics = await testWorkerMode(markdown, 20);
await new Promise(resolve => setTimeout(resolve, 500));
const mainMetrics = await testMainThreadMode(markdown, 20);
displayComparison(workerMetrics, mainMetrics, '中文本测试');
}
async function runLongTextTest() {
document.getElementById('console-log').innerHTML = '';
log('========================================', 'info');
log('开始测试:长文本 (20000字)', 'info');
log('========================================', 'info');
const markdown = generateMarkdown(20000);
log(`生成了 ${markdown.length} 字符的测试文本`, 'info');
const workerMetrics = await testWorkerMode(markdown, 10);
await new Promise(resolve => setTimeout(resolve, 500));
const mainMetrics = await testMainThreadMode(markdown, 10);
displayComparison(workerMetrics, mainMetrics, '长文本测试');
}
// 运行所有测试
async function runAllTests() {
await runShortTextTest();
await new Promise(resolve => setTimeout(resolve, 1000));
await runMediumTextTest();
await new Promise(resolve => setTimeout(resolve, 1000));
await runLongTextTest();
log('========================================', 'success');
log('所有测试完成!', 'success');
log('========================================', 'success');
}
// 查看统计
function viewStats() {
const stats = window.markdownWorkerPool.getStats();
console.log('Worker Pool 统计:', stats);
alert(`Worker Pool 统计信息:
池大小: ${stats.poolSize}
初始化: ${stats.initialized ? '是' : '否'}
可用: ${stats.available ? '是' : '否'}
已完成任务: ${stats.tasksCompleted}
失败任务: ${stats.tasksFailed}
降级任务: ${stats.tasksFallback}
队列长度: ${stats.queueLength}
待处理: ${stats.pendingTasks}
平均 Worker 耗时: ${stats.avgWorkerTime.toFixed(2)} ms
平均主线程耗时: ${stats.avgMainThreadTime.toFixed(2)} ms
详细信息已输出到控制台`);
}
// 页面加载完成
window.addEventListener('load', function() {
console.log('%c═══════════════════════════════════════', 'color: #4CAF50; font-weight: bold');
console.log('%c Phase 5.1.2 Markdown Worker 测试', 'color: #4CAF50; font-weight: bold');
console.log('%c═══════════════════════════════════════', 'color: #4CAF50; font-weight: bold');
console.log('');
console.log('Worker 池状态:', window.markdownWorkerPool ? '已初始化' : '未初始化');
console.log('');
console.log('使用方法:');
console.log(' 1. 点击"运行所有测试"进行完整测试');
console.log(' 2. 或选择单独的测试场景');
console.log(' 3. 查看对比结果和性能指标');
console.log('');
log('页面加载完成,准备就绪', 'success');
log(`Worker 池已初始化,大小: ${window.markdownWorkerPool.config.poolSize}`, 'info');
});
</script>
</body>
</html>