paper-burner/js/ui/chunk_compare_performance_t...

686 lines
23 KiB
JavaScript
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.

/**
* 分块对比性能测试脚本
* 用于测试和验证分块对比预览的性能改进
*/
class ChunkComparePerformanceTester {
constructor() {
this.testResults = [];
this.isRunning = false;
}
/**
* 运行完整的性能测试套件
*/
async runFullTestSuite() {
if (this.isRunning) {
console.warn('[PerformanceTest] 测试正在进行中,请等待完成');
return;
}
this.isRunning = true;
console.log('[PerformanceTest] 开始运行性能测试套件');
try {
const results = {
testTime: new Date().toISOString(),
browser: this.getBrowserInfo(),
device: this.getDeviceInfo(),
tests: {}
};
// 测试不同数量的分块
const chunkCounts = [5, 10, 20, 50, 100];
for (const count of chunkCounts) {
console.log(`[PerformanceTest] 测试 ${count} 个分块的性能`);
results.tests[`chunks_${count}`] = await this.testChunkRendering(count);
// 让浏览器有时间清理
await this.delay(1000);
}
// 测试内存使用情况
results.memoryTest = this.testMemoryUsage();
// 测试滚动性能
results.scrollTest = await this.testScrollPerformance();
// 生成测试报告
this.generateTestReport(results);
console.log('[PerformanceTest] 性能测试套件完成');
return results;
} catch (error) {
console.error('[PerformanceTest] 测试过程中出错:', error);
} finally {
this.isRunning = false;
}
}
/**
* 测试分块渲染性能
*/
async testChunkRendering(chunkCount) {
const testData = this.generateTestData(chunkCount);
const container = this.createTestContainer();
// 测试原有渲染方法
const originalResult = await this.testOriginalRendering(testData, container);
// 清理
container.innerHTML = '';
await this.delay(100);
// 测试优化后的渲染方法
const optimizedResult = await this.testOptimizedRendering(testData, container);
// 清理
container.remove();
const improvement = {
renderTime: {
original: originalResult.renderTime,
optimized: optimizedResult.renderTime,
improvement: ((originalResult.renderTime - optimizedResult.renderTime) / originalResult.renderTime * 100).toFixed(2) + '%'
},
memoryUsage: {
original: originalResult.memoryUsage,
optimized: optimizedResult.memoryUsage,
reduction: originalResult.memoryUsage > 0 ? ((originalResult.memoryUsage - optimizedResult.memoryUsage) / originalResult.memoryUsage * 100).toFixed(2) + '%' : 'N/A'
},
domNodes: {
original: originalResult.domNodes,
optimized: optimizedResult.domNodes,
reduction: ((originalResult.domNodes - optimizedResult.domNodes) / originalResult.domNodes * 100).toFixed(2) + '%'
}
};
console.log(`[PerformanceTest] ${chunkCount}个分块测试结果:`, improvement);
return improvement;
}
/**
* 生成测试数据
*/
generateTestData(chunkCount) {
const ocrChunks = [];
const translatedChunks = [];
for (let i = 0; i < chunkCount; i++) {
// 生成不同长度的测试内容
const contentLength = Math.floor(Math.random() * 1000) + 200; // 200-1200字符
const ocrContent = this.generateTestContent(contentLength, 'ocr', i);
const transContent = this.generateTestContent(contentLength, 'translation', i);
ocrChunks.push(ocrContent);
translatedChunks.push(transContent);
}
return {
ocrChunks,
translatedChunks,
images: []
};
}
/**
* 生成测试内容
*/
generateTestContent(length, type, index) {
const headings = [
'# 主要标题',
'## 次要标题',
'### 三级标题'
];
const paragraphs = [
'这是一段测试文本,用于验证分块对比功能的性能表现。',
'本段包含**粗体文字**和*斜体文字*,以及`代码片段`。',
'测试内容包括数学公式:$E = mc^2$,以及更复杂的公式:$$\\int_{-\\infty}^{\\infty} e^{-x^2} dx = \\sqrt{\\pi}$$',
'这里有一个代码块:\n```javascript\nfunction test() {\n console.log("Hello World");\n}\n```',
'列表项目:\n- 第一项\n- 第二项\n- 第三项',
'表格测试:\n| 列1 | 列2 | 列3 |\n|-----|-----|-----|\n| 值1 | 值2 | 值3 |'
];
let content = `${headings[index % headings.length]} (${type} #${index + 1})\n\n`;
while (content.length < length) {
content += paragraphs[Math.floor(Math.random() * paragraphs.length)] + '\n\n';
}
return content.substring(0, length);
}
/**
* 创建测试容器
*/
createTestContainer() {
const container = document.createElement('div');
container.id = 'performance-test-container';
container.style.cssText = `
position: absolute;
top: -10000px;
left: -10000px;
width: 1000px;
height: 600px;
overflow: hidden;
`;
document.body.appendChild(container);
return container;
}
/**
* 测试原有渲染方法
*/
async testOriginalRendering(testData, container) {
const startTime = performance.now();
const startMemory = this.getMemoryUsage();
// 模拟原有的同步渲染
let html = '<div class="chunk-compare-container">';
for (let i = 0; i < testData.ocrChunks.length; i++) {
html += `
<div class="chunk-pair">
<div class="block-outer" id="block-${i}">
<h4>分块 ${i + 1}</h4>
<div class="original-chunk-content">
<div class="ocr-content">${this.renderMarkdownSync(testData.ocrChunks[i])}</div>
<div class="trans-content">${this.renderMarkdownSync(testData.translatedChunks[i])}</div>
</div>
</div>
</div>
`;
}
html += '</div>';
container.innerHTML = html;
const endTime = performance.now();
const endMemory = this.getMemoryUsage();
const domNodes = container.querySelectorAll('*').length;
return {
renderTime: endTime - startTime,
memoryUsage: endMemory - startMemory,
domNodes: domNodes
};
}
/**
* 测试优化后的渲染方法
*/
async testOptimizedRendering(testData, container) {
const startTime = performance.now();
const startMemory = this.getMemoryUsage();
// 使用优化器进行渲染
if (window.ChunkCompareOptimizer && window.ChunkCompareOptimizer.instance) {
const optimizedHTML = window.ChunkCompareOptimizer.instance.optimizeChunkComparison(
testData.ocrChunks,
testData.translatedChunks,
{ images: testData.images, isOriginalFirst: true }
);
container.innerHTML = optimizedHTML;
// 等待异步渲染完成
await this.waitForOptimizedRendering(container);
} else {
// 回退到预览模式
let html = '<div class="chunk-compare-container">';
for (let i = 0; i < Math.min(testData.ocrChunks.length, 5); i++) {
html += `
<div class="chunk-pair optimized-chunk">
<div class="chunk-header">
<h4>分块 ${i + 1}</h4>
</div>
<div class="chunk-preview-container" data-lazy-load="true">
<div class="chunk-preview">${this.getContentPreview(testData.ocrChunks[i])}</div>
<div class="load-full-content-btn">点击加载完整内容</div>
</div>
</div>
`;
}
html += '</div>';
container.innerHTML = html;
}
const endTime = performance.now();
const endMemory = this.getMemoryUsage();
const domNodes = container.querySelectorAll('*').length;
return {
renderTime: endTime - startTime,
memoryUsage: endMemory - startMemory,
domNodes: domNodes
};
}
/**
* 等待优化后的渲染完成
*/
async waitForOptimizedRendering(container) {
let maxWait = 5000; // 最多等待5秒
const interval = 100;
while (maxWait > 0) {
const loadingIndicators = container.querySelectorAll('.chunk-loading-indicator');
if (loadingIndicators.length === 0) {
break;
}
await this.delay(interval);
maxWait -= interval;
}
}
/**
* 同步渲染Markdown简化版
*/
renderMarkdownSync(content) {
if (!content) return '';
// 简化的Markdown渲染避免复杂的异步操作
return content
.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>')
.replace(/\*(.*?)\*/g, '<em>$1</em>')
.replace(/`(.*?)`/g, '<code>$1</code>')
.replace(/\n/g, '<br>');
}
/**
* 获取内容预览
*/
getContentPreview(content) {
if (!content) return '(空内容)';
return content.length > 100 ? content.substring(0, 100) + '...' : content;
}
/**
* 测试内存使用情况
*/
testMemoryUsage() {
if (!performance.memory) {
return {
error: '浏览器不支持内存监控',
supported: false
};
}
const memory = performance.memory;
return {
supported: true,
usedJSHeapSize: (memory.usedJSHeapSize / 1024 / 1024).toFixed(2) + ' MB',
totalJSHeapSize: (memory.totalJSHeapSize / 1024 / 1024).toFixed(2) + ' MB',
jsHeapSizeLimit: (memory.jsHeapSizeLimit / 1024 / 1024).toFixed(2) + ' MB',
usage: ((memory.usedJSHeapSize / memory.jsHeapSizeLimit) * 100).toFixed(2) + '%'
};
}
/**
* 测试滚动性能
*/
async testScrollPerformance() {
const container = document.querySelector('.chunk-compare-container');
if (!container) {
return { error: '未找到分块对比容器' };
}
const scrollTests = [];
const scrollDistance = 100;
const testCount = 10;
for (let i = 0; i < testCount; i++) {
const startTime = performance.now();
// 模拟滚动
container.scrollTop += scrollDistance;
// 等待渲染完成
await new Promise(resolve => requestAnimationFrame(resolve));
const endTime = performance.now();
scrollTests.push(endTime - startTime);
}
// 重置滚动位置
container.scrollTop = 0;
const avgScrollTime = scrollTests.reduce((a, b) => a + b, 0) / scrollTests.length;
const maxScrollTime = Math.max(...scrollTests);
const minScrollTime = Math.min(...scrollTests);
return {
averageTime: avgScrollTime.toFixed(2) + 'ms',
maxTime: maxScrollTime.toFixed(2) + 'ms',
minTime: minScrollTime.toFixed(2) + 'ms',
testCount: testCount
};
}
/**
* 获取内存使用量
*/
getMemoryUsage() {
if (performance.memory) {
return performance.memory.usedJSHeapSize;
}
return 0;
}
/**
* 获取浏览器信息
*/
getBrowserInfo() {
const ua = navigator.userAgent;
let browser = 'Unknown';
if (ua.indexOf('Chrome') > -1) browser = 'Chrome';
else if (ua.indexOf('Firefox') > -1) browser = 'Firefox';
else if (ua.indexOf('Safari') > -1) browser = 'Safari';
else if (ua.indexOf('Edge') > -1) browser = 'Edge';
return {
name: browser,
userAgent: ua,
vendor: navigator.vendor,
language: navigator.language
};
}
/**
* 获取设备信息
*/
getDeviceInfo() {
return {
platform: navigator.platform,
hardwareConcurrency: navigator.hardwareConcurrency || 'Unknown',
deviceMemory: navigator.deviceMemory || 'Unknown',
screenResolution: `${screen.width}x${screen.height}`,
viewportSize: `${window.innerWidth}x${window.innerHeight}`
};
}
/**
* 生成测试报告
*/
generateTestReport(results) {
console.group('[PerformanceTest] 性能测试报告');
console.log('测试时间:', results.testTime);
console.log('浏览器信息:', results.browser);
console.log('设备信息:', results.device);
console.group('分块渲染测试结果:');
Object.entries(results.tests).forEach(([testName, result]) => {
console.group(testName);
console.log('渲染时间改善:', result.renderTime.improvement);
console.log('内存使用减少:', result.memoryUsage.reduction);
console.log('DOM节点减少:', result.domNodes.reduction);
console.groupEnd();
});
console.groupEnd();
console.log('内存测试:', results.memoryTest);
console.log('滚动性能测试:', results.scrollTest);
console.groupEnd();
// 将结果保存到 localStorage
localStorage.setItem('chunkComparePerformanceResults', JSON.stringify(results));
// 显示用户友好的报告
this.showUserReport(results);
}
/**
* 显示用户友好的测试报告
*/
showUserReport(results) {
const reportEl = document.createElement('div');
reportEl.className = 'performance-report-modal';
reportEl.innerHTML = `
<div class="report-overlay">
<div class="report-content">
<h3>分块对比性能测试报告</h3>
<div class="report-summary">
<div class="summary-item">
<span class="label">测试时间:</span>
<span class="value">${new Date(results.testTime).toLocaleString()}</span>
</div>
<div class="summary-item">
<span class="label">浏览器:</span>
<span class="value">${results.browser.name}</span>
</div>
<div class="summary-item">
<span class="label">总体评价:</span>
<span class="value performance-grade">${this.calculateOverallGrade(results)}</span>
</div>
</div>
<div class="test-results">
<h4>性能改进详情</h4>
${this.generateResultsHTML(results.tests)}
</div>
<div class="report-actions">
<button id="close-performance-report">关闭</button>
<button id="export-performance-report">导出报告</button>
</div>
</div>
</div>
`;
// 添加样式
const style = document.createElement('style');
style.id = 'performance-report-style'; // 添加ID以便后续清理
style.textContent = `
.performance-report-modal {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 10000;
}
.report-overlay {
background: rgba(0,0,0,0.5);
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
}
.report-content {
background: white;
max-width: 600px;
max-height: 80vh;
overflow-y: auto;
border-radius: 8px;
padding: 24px;
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
}
.report-summary {
margin: 16px 0;
border: 1px solid #e2e8f0;
border-radius: 6px;
padding: 16px;
}
.summary-item {
display: flex;
justify-content: space-between;
margin-bottom: 8px;
}
.performance-grade {
font-weight: bold;
color: #059669;
}
.test-results {
margin: 16px 0;
}
.result-item {
padding: 8px;
border-bottom: 1px solid #f1f5f9;
}
.report-actions {
display: flex;
gap: 12px;
justify-content: flex-end;
margin-top: 20px;
}
.report-actions button {
padding: 8px 16px;
border: 1px solid #d1d5db;
border-radius: 4px;
background: white;
cursor: pointer;
}
.report-actions button:first-child {
background: #f3f4f6;
}
.report-actions button:last-child {
background: #3b82f6;
color: white;
border-color: #3b82f6;
}
`;
document.head.appendChild(style);
document.body.appendChild(reportEl);
// 添加事件监听器
const closeBtn = document.getElementById('close-performance-report');
const exportBtn = document.getElementById('export-performance-report');
const overlay = reportEl.querySelector('.report-overlay');
// 关闭按钮事件
closeBtn.addEventListener('click', () => {
this.closeReport();
});
// 导出按钮事件
exportBtn.addEventListener('click', () => {
this.exportReport();
});
// 点击遮罩层关闭
overlay.addEventListener('click', (e) => {
if (e.target === overlay) {
this.closeReport();
}
});
// ESC键关闭
const escHandler = (e) => {
if (e.key === 'Escape') {
this.closeReport();
document.removeEventListener('keydown', escHandler);
}
};
document.addEventListener('keydown', escHandler);
}
/**
* 关闭性能报告
*/
closeReport() {
const reportEl = document.querySelector('.performance-report-modal');
const styleEl = document.getElementById('performance-report-style');
if (reportEl) {
reportEl.remove();
}
if (styleEl) {
styleEl.remove();
}
}
/**
* 计算总体评分
*/
calculateOverallGrade(results) {
let totalImprovement = 0;
let testCount = 0;
Object.values(results.tests).forEach(test => {
const improvement = parseFloat(test.renderTime.improvement);
if (!isNaN(improvement)) {
totalImprovement += improvement;
testCount++;
}
});
const avgImprovement = testCount > 0 ? totalImprovement / testCount : 0;
if (avgImprovement >= 50) return '优秀 (A)';
if (avgImprovement >= 30) return '良好 (B)';
if (avgImprovement >= 10) return '一般 (C)';
return '需改进 (D)';
}
/**
* 生成结果HTML
*/
generateResultsHTML(tests) {
return Object.entries(tests).map(([testName, result]) => `
<div class="result-item">
<strong>${testName.replace('chunks_', '')}个分块:</strong>
<div>渲染速度提升: ${result.renderTime.improvement}</div>
<div>内存使用减少: ${result.memoryUsage.reduction}</div>
<div>DOM节点减少: ${result.domNodes.reduction}</div>
</div>
`).join('');
}
/**
* 导出测试报告
*/
exportReport() {
const results = localStorage.getItem('chunkComparePerformanceResults');
if (!results) {
alert('没有可导出的测试结果');
return;
}
const blob = new Blob([results], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `chunk-compare-performance-${Date.now()}.json`;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
}
/**
* 延迟函数
*/
delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
}
// 创建全局测试实例
window.ChunkComparePerformanceTester = new ChunkComparePerformanceTester();
// 添加控制台快捷命令
console.log('%c分块对比性能测试器已加载', 'color: #059669; font-weight: bold');
console.log('使用 window.ChunkComparePerformanceTester.runFullTestSuite() 运行完整测试');
// 如果在开发环境,自动运行测试
if (window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1') {
// 页面加载完成后自动运行测试
window.addEventListener('load', () => {
setTimeout(() => {
if (document.querySelector('.chunk-compare-container')) {
console.log('[PerformanceTest] 检测到分块对比页面,自动运行性能测试');
window.ChunkComparePerformanceTester.runFullTestSuite();
}
}, 2000);
});
}