-
Notifications
You must be signed in to change notification settings - Fork 295
Expand file tree
/
Copy pathtest_cctv_header_large.js
More file actions
167 lines (130 loc) · 6.82 KB
/
test_cctv_header_large.js
File metadata and controls
167 lines (130 loc) · 6.82 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
import fs from 'fs/promises';
import path from 'path';
import { fileURLToPath } from 'url';
import FileHeaderManager from '../../utils/fileHeaderManager.js';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const sourceFile = path.resolve(__dirname, '../../spider/js/央视大全[官].js');
const testFile = path.resolve(__dirname, 'test_cctv_header_large_temp.js');
// 模拟 Buffer 模式的 Header 移除
async function removeHeaderBuffer(filePath) {
const handle = await fs.open(filePath, 'r');
try {
const stats = await handle.stat();
const buffer = Buffer.alloc(stats.size);
await handle.read(buffer, 0, stats.size, 0);
// 查找注释块
// /* = 0x2f 0x2a
// */ = 0x2a 0x2f
const start = buffer.indexOf('/*');
if (start === -1) return;
const end = buffer.indexOf('*/', start);
if (end === -1) return;
// 提取注释内容进行检查
const commentBuf = buffer.subarray(start, end + 2);
const commentStr = commentBuf.toString('utf8');
if (!commentStr.includes('@header(')) return;
// 查找 @header
const headerStart = commentStr.indexOf('@header(');
// 这里简化处理:假设我们要移除整个 @header(...)
// 实际需要括号平衡逻辑,这里简单起见,假设只有一层括号或用 regex
// 为了公平对比,我们只测试 "读取 Buffer -> 查找位置 -> 拼接 Buffer -> 写入" 的过程
// 模拟:我们找到了 header 的 start 和 end
// 实际逻辑应该复用 FileHeaderManager.findHeaderBlock
// 但 findHeaderBlock 操作的是 string。
// 关键在于:是否可以只把 commentBlock 转 string,找到 offset,
// 然后在原始 Buffer 上进行 slice 和 concat?
// 假设 headerStartOffset 和 headerEndOffset 是相对于 buffer 的
const headerGlobalStart = start + headerStart;
// 简单模拟 header 长度为 100 字节
const headerGlobalEnd = headerGlobalStart + 100;
const newBuf = Buffer.concat([
buffer.subarray(0, headerGlobalStart),
buffer.subarray(headerGlobalEnd)
]);
await fs.writeFile(filePath, newBuf);
} finally {
await handle.close();
}
}
async function runTest() {
console.log('=== 开始大文件性能对比测试 ===');
// 1. 创建大文件 (500KB)
console.log('正在生成 500KB 测试文件...');
let content = await fs.readFile(sourceFile, 'utf8');
// 重复内容直到达到 500KB
while (Buffer.byteLength(content) < 500 * 1024) {
content += '\n// Padding content to increase file size...\n' + content;
}
// 确保头部有 @header
if (!content.includes('@header(')) {
console.warn('源文件没有 @header,测试可能无效');
}
await fs.writeFile(testFile, content);
const stats = await fs.stat(testFile);
console.log(`测试文件大小: ${(stats.size / 1024).toFixed(2)} KB`);
const iterations = 100; // 大文件操作慢,减少次数
// 2. 测试现有 String 模式 (FileHeaderManager.removeHeader)
console.log(`\n[String Mode] removeHeader 测试 (${iterations} 次)...`);
// 预热
const header = await FileHeaderManager.readHeader(testFile);
const startString = performance.now();
for (let i = 0; i < iterations; i++) {
// removeHeader 目前只返回字符串,不写入
// 为了模拟真实场景,我们需要包含 fs.readFile 和 fs.writeFile
// FileHeaderManager.removeHeader 内部如果不传 path 传 content,则不包含读。
// 如果传 path,则包含读。
// 但它不包含写。
// 模拟完整流程:读 -> 处理 -> 写
const newContent = await FileHeaderManager.removeHeader(testFile); // 包含 readFile
await fs.writeFile(testFile, newContent);
// 恢复文件头以便下一次循环 (为了测试 remove,必须先有)
// 这步不计入时间?不,这很麻烦。
// 我们可以测试 "读取 -> 替换(即使没找到也算处理) -> 写入" 的开销
// 或者只测试 removeHeader 的处理部分。
// 为了简单,我们测试 "Write Header" 吧,因为 writeHeader 包含 读 -> 改 -> 写
await FileHeaderManager.writeHeader(testFile, header, { createBackup: false });
}
const endString = performance.now();
const timeString = endString - startString;
console.log(`String Mode 总耗时: ${timeString.toFixed(2)} ms`);
console.log(`String Mode 平均耗时: ${(timeString / iterations).toFixed(2)} ms`);
// 3. 测试模拟 Buffer 模式
// 由于 FileHeaderManager 没有 Buffer 模式,我们只能模拟 "读 Buffer -> 改 Buffer -> 写 Buffer"
// 对比 "读 String -> 改 String -> 写 String"
console.log(`\n[Buffer Mode (Simulated)] writeHeader 测试 (${iterations} 次)...`);
const startBuffer = performance.now();
for (let i = 0; i < iterations; i++) {
const handle = await fs.open(testFile, 'r');
const bufStats = await handle.stat();
const buffer = Buffer.alloc(bufStats.size);
await handle.read(buffer, 0, bufStats.size, 0);
await handle.close();
// 模拟修改:在 Buffer 中找到 header 位置并替换
// 实际算法:
// 1. 找到 comment block (buffer.indexOf) -> 极快
// 2. 将 comment block 转 string (很短) -> 极快
// 3. 在 comment string 中找 header -> 极快
// 4. 拼接 Buffer -> 极快 (不需要编解码大段代码)
const startComment = buffer.indexOf('/*');
const endComment = buffer.indexOf('*/', startComment);
// 假设我们找到了 offset,进行拼接
// 这里不做真实解析,只做 Buffer 操作模拟开销
const newBuf = Buffer.concat([
buffer.subarray(0, startComment),
Buffer.from('/* Updated Header */'), // 模拟新头
buffer.subarray(endComment + 2)
]);
await fs.writeFile(testFile, newBuf);
// 恢复 (其实上面已经写入了,不需要额外恢复,因为每次都覆盖)
}
const endBuffer = performance.now();
const timeBuffer = endBuffer - startBuffer;
console.log(`Buffer Mode 总耗时: ${timeBuffer.toFixed(2)} ms`);
console.log(`Buffer Mode 平均耗时: ${(timeBuffer / iterations).toFixed(2)} ms`);
const improvement = ((timeString - timeBuffer) / timeString * 100).toFixed(2);
console.log(`\n🚀 预计 Buffer 模式提升: ${improvement}%`);
// 清理
await fs.unlink(testFile);
}
runTest();