性能
shikiphp 使用纯 PHP 进行真正的 TextMate 分词,因此需要如实说明它在哪些情况下速度快、哪些情况下速度不快。
性能表现
每个文件预计耗时数十到数百毫秒,而非微秒。分词器会缓存已编译的规则和扫描器,语法按需加载(从未涉及嵌入式语言的示例无需支付解码开销),大多数语法模式会通过经过等价性验证的 PCRE 快速路径使用原生 preg_*,其余部分则由纯 PHP 匹配器处理(参见 架构)。
以下是仓库基准测试(bench/bench.php,主题 github-dark)在一台机器上测得的预热后单次调用耗时,语料文件约有 55 到 105 行:
| 语法 | PHP 8.2 | PHP 8.4 |
|---|---|---|
| JSON, YAML | 30–65 ms | 25–45 ms |
| CSS, HTML | 100–120 ms | 80–100 ms |
| Python, Rust, Go, Bash, Markdown | 190–220 ms | 125–175 ms |
| PHP | 约 290 ms | 约 170 ms |
| TypeScript, TSX | 490–510 ms | 310–370 ms |
进程中首次调用某种语言时,还需支付一次性的语法加载和编译开销:在同一批运行中,JSON耗时 50–70 ms,Markdown则略超一秒。开销随语法复杂度和输入大小变化,且数据会受硬件影响,因此请在自己的环境中测量:克隆仓库后,运行 composer install,然后运行 composer bench。
安全防护
正则表达式引擎包含失败记忆化和 ReDoS 防护,因此遇到病态模式时会变慢,而不会卡死。你还可以使用 tokenizeMaxLineLength限制每行的处理量;达到或超过指定长度的行会输出为单个纯文本令牌,以防范恶意构造的单行输入。
在生产环境中缓存
确保代码高亮不占用请求处理路径的可靠方法,就是不要在每次请求时都运行它。相同代码使用相同选项进行高亮,总会生成相同的 HTML,因此非常适合缓存:
- 对静态网站和文档,在构建时进行高亮。
- 缓存渲染后的 HTML(以代码和选项为键),用于用户提供或存储在数据库中的代码片段,并在源代码变更前重复使用。
在进程中重复使用同一个高亮器(Shikiphp门面会为你处理),以便在多次调用间共享语法和主题缓存。