跳转到正文

性能

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门面会为你处理),以便在多次调用间共享语法和主题缓存。