问题所在
每次 AI 流式输出新的文本块时,传统的 markdown 解析器都会从头开始重新解析整个文档——在已经渲染的内容上浪费 CPU 资源。Incremark 通过只解析新增内容来解决这个问题。
基准测试结果
较短的 Markdown 文档:
较长的 Markdown 文档:
说明:由于分块策略的影响,每次基准测试的性能提升倍数可能有所不同。演示页面使用随机块长度:const chunks = content.match(/[\s\S]{1,20}/g) || []。这种分块方式会影响稳定块的生成,更好地模拟真实场景(一个块可能包含前一个或后一个块的内容)。无论如何分块,性能提升都是有保证的。演示网站没有使用任何有利于偏向自身性能展示的分块策略来夸大结果。
在线演示:
对于超长的 markdown 文档,性能提升更加惊人。20KB 的 markdown 基准测试实现了令人难以置信的 46 倍速度提升。内容越长,提速越显著——理论上没有上限。
核心优势
⚡ 通常 2-10 倍提速 - 针对 AI 流式场景
🚀 更大的提速 - 对于更长的文档(测试最高达 46 倍)
🎯 零冗余解析 - 每个字符最多只解析一次
✨ 完美适配 AI 流式 - 专为增量更新优化
💪 也适用于普通 markdown - 不仅限于 AI 场景
🔧 框架支持 - 包含 React 和 Vue 组件
为什么这么快?
传统解析器的问题
开发过 AI 聊天应用的小伙伴都知道,AI 流式输出会将内容分成小块传输到前端。每次接收到新块后,整个 markdown 字符串都必须喂给 markdown 解析器(无论是 remark 、marked.js 还是 markdown-it )。这些解析器每次都会重新解析整个 markdown 文档,即使是那些已经渲染且稳定的部分。这造成了很多不必要的的性能浪费。
像 vue-stream-markdown 这样的工具在渲染层做了努力,将稳定的 token 渲染为稳定的组件,只更新不稳定的组件,从而在 UI 层实现流畅的流式输出。
然而,这仍然无法解决根本的性能问题:markdown 文本的重复解析。这才是真正吞噬 CPU 性能的元凶。输出文档越长,性能浪费越严重。
Incremark 的核心性能优化
除了在 UI 渲染层实现组件复用和流畅更新外,incremark 的关键创新在于 markdown 解析:只解析不稳定的 markdown 块,永不重新解析稳定的块。这将解析复杂度从 **O(n²) 降低到 O(n)**。理论上,输出越长,性能提升越大。
1. 增量解析:从 O(n²) 到 O(n)
传统解析器每次都重新解析整个文档,导致解析工作量呈二次方增长。Incremark 的 IncremarkParser 类采用增量解析策略(参见 IncremarkParser.ts):
// 设计思路:
// 1. 维护一个文本缓冲区来接收流式输入
// 2. 识别"稳定边界"并将已完成的块标记为 'completed'
// 3. 对于正在接收的块,只重新解析该块的内容
// 4. 复杂的嵌套节点作为一个整体处理,直到确认完成
2. 智能边界检测
append 函数中的 findStableBoundary() 方法是关键优化点:
append(chunk: string): IncrementalUpdate {
this.buffer += chunk
this.updateLines()
const { line: stableBoundary, contextAtLine } = this.findStableBoundary()
if (stableBoundary >= this.pendingStartLine && stableBoundary >= 0) {
// 只解析新完成的块,永不重新解析已完成的内容
const stableText = this.lines.slice(this.pendingStartLine, stableBoundary + 1).join('\n')
const ast = this.parse(stableText)
// ...
}
}
3. 状态管理避免冗余计算
解析器维护几个关键状态来消除重复工作:
4. 增量行更新优化
updateLines() 方法只处理新内容,避免全量 split 操作:
private updateLines(): void {
// 找到最后一个不完整的行(可能被新块续上)
const lastLineStart = this.lineOffsets[prevLineCount - 1]
const textFromLastLine = this.buffer.slice(lastLineStart)
// 只重新 split 最后一行及其后续内容
const newLines = textFromLastLine.split('\n')
// 只更新变化的部分
}
性能对比
这种设计在实际测试中表现卓越:
[td]文档大小[/td]
[td]传统解析器(字符数)[/td]
[td]Incremark (字符数)[/td]
[td]减少比例[/td]
1KB
1,010,000
20,000
98%
5KB
25,050,000
100,000
99.6%
20KB
400,200,000
400,000
99.9%
关键不变量
Incremark 的性能优势源于一个关键不变量:一旦块被标记为 completed ,就永远不会被重新解析。这确保了每个字符最多只被解析一次,实现了 O(n) 的时间复杂度。
🚀 立即开始
停止在冗余解析上浪费 CPU 资源。立即尝试 incremark:
快速安装
npm install @incremark/core
# React 版本
npm install @incremark/react
# Vue 版本
npm install @incremark/vue
资源链接
使用场景
完美适用于:
无论你是在构建 AI 界面还是只是想要更快的 markdown 渲染,incremark 都能提供你需要的性能。
欢迎体验与支持
非常欢迎尝试与体验,在线演示是感受速度提升最直观的方式:
如果你觉得 incremark 有用并想要参与改进,也欢迎提交 issue 与独特想法!GitHub Issues

