Sukka's Blog

童话只美在真实却从不续写

速度就是关键! —— 我们是如何让 Hexo 4.2 的生成速度提升 30% 的

Sukka's Avatar 2019-12-23 笔记本

  1. 1. Benchmark
    1. 1.1. Node.js 8
    2. 1.2. Node.js 10
    3. 1.3. Node.js 12
    4. 1.4. Node.js 13
  2. 2. 从 Hexo 中去除 cheerio 依赖
  3. 3. 改善 Cache of Rendered HTML 机制
  4. 4. 从 Hexo 中去除 Lodash 依赖
  5. 5. 缓存实用函数的返回值
  6. 6. 未来

本文已翻译为英文并收录于 Hexo 官网:Speed is the Key - How We Make Hexo 30% Faster

对于 Hexo 来说,速度一直都是关键。三年前,通过模板预编译,Hexo 3.2 的生成性能相比 Hexo 3.1 提升了一倍。到了 Hexo 4.2,通过一系列改进,我们成功使 Hexo 4.2 的生成速度相比 Hexo 3.2 再提升了 30%。

Benchmark

Benchmark 的设置和环境如下:

  • Travis CI - Ubuntu Xenial 16.04
    • CPU:2 Cores
    • RAM:7.5 GB
  • Hexo 默认的 landscape 主题
  • 随机产生的 300 篇文章:每篇文章都包含了所有的 Markdown 语法(标题、链接、图片),用于测试 highlight.js 的代码块;Front Matter 中设置了不重复的一个分类和三个标签。

由于 Hexo 3.2 开始将渲染结果存在 db.json 中,因此在 Benchmark 中同时测试了冷生成(heox g 之前先 hexo clean 删除 db.json)和热生成(第二次 hexo g 之前不执行 hexo clean)的性能数据。

每次 Benchmark 以 Cold => Hot 的顺序执行;内存占用使用 time 测量,取 Resident Set Size (RSS) 的值。

Benchmark 使用的脚本可以在 这里 查看。

Node.js 8

Hexo 3.2Hexo 3.8Hexo 4.2
Cold processing13.585s0%18.572s+37%9.210s-32%
Cold generation13.027s0%50.528s+284%8.666s-33%
Memory Usage (Cold)815.754MB0%1416.309MB+69%605.312MB-26%
Hot processing0.668s0%0.712s+6%0.732s+7%
Hot generation11.734s0%46.339s+295%7.821s-33%
Memory Usage (Hot)702.535MB0%1450.719MB+106%821.512MB+17%

Node.js 10

Hexo 3.2Hexo 3.8Hexo 4.2
Cold processing11.875s0%15.985s+35%8.043s-29%
Cold generation10.308s0%41.339s+301%7.450s-28%
Memory Usage (Cold)805.633MB0%1440.297MB+79%599.008MB-26%
Hot processing0.700s0%0.676s-3%0.731s+4%
Hot generation8.322s0%35.453s+326%6.420s-23%
Memory Usage (Hot)679.082MB0%1447.109MB+113%789.527MB+16%

Node.js 12

Hexo 3.2Hexo 3.8Hexo 4.2
Cold processing11.454s0%15.626s+36%8.381s-27%
Cold generation10.428s0%37.482s+260%7.283s-30%
Memory Usage (Cold)1101.586MB0%1413.359MB+28%580.953MB-47%
Hot processing0.724s0%0.790s+9%0.790s+9%
Hot generation8.994s0%35.116s+293%6.385s-29%
Memory Usage (Hot)696.500MB0%1538.719MB+120%600.398MB-14%

Node.js 13

Hexo 3.2Hexo 3.8Hexo 4.2
Cold processing11.496s0%14.970s+29%8.489s-26%
Cold generation10.088s0%36.867s+265%7.212s-28%
Memory Usage (Cold)1104.465MB0%1418.273MB+28%596.233MB-46%
Hot processing0.724s0%0.776s+7%0.756s+4%
Hot generation7.995s0%33.968s+325%6.294s-21%
Memory Usage (Hot)761.195MB0%1516.078MB+99%812.234MB+7%

从 Hexo 中去除 cheerio 依赖

正如 Benchmark 数据所示,Hexo 3.8 出现了严重的性能下降。我们发现 #3129 引入的 meta_generator 特性是罪魁祸首。#3129 使用 cheerio 往 HTML 的 <head> 中插入 <meta name="generator" content="Hexo [version]">,使得 cheerio 需要将 Hexo 生成的所有 HTML 全部存进内存并解析成 DOM。

cheerio 很快,但是在遍历上百个 HTML 文件时时还是会遇到性能瓶颈。我们在 #3677 中提出提案去除 cheerio 依赖。先后经过 #3671#3680#3685,Hexo 在 open_graph() helper、meta_generator filter 和 external_link filter 中用正则表达式替代了 cheerio,并在 hexo-util#137#3850 中将 toc() 所依赖的 cheerio 换成了更快的 htmlparser2。到了 Hexo 4.2,我们从 Hexo 中彻底去掉了 cheerio

改善 Cache of Rendered HTML 机制

Cache of Rendered HTML 最早于 Hexo 3.0.0-rc4 中 e8e45ed 引入,试图通过缓存渲染结果来改善 Hexo 的生成性能。但是在 hexo g 中每一条路径只被使用一次,所以缓存并没有起到效果、白白占用了内存。在 #3756Cache of Rendered HTML 被调整为只在 hexo s 下启用,大幅减少了 hexo g 的内存占用。

从 Hexo 中去除 Lodash 依赖

Lodash 是个实用的工具库,大大降低了 Array、Number、Objects、String 的使用难度。不过随着 ES6 的新特性不断增加,Lodash 的大部分功能都有了 Native 替代。

Hexo 其实很早就开始减少对 Lodash 的依赖了,如 #3285#3290warehouse#18。在 #3753 中,我们提出可以参考 You don’t (may not) need Lodash/Underscore 逐步将 Lodash 替换为 Native JavaScript。经过 #3785#3786#3788#3790#3791#3809#3810#3813#3826#3845hexo-util#141#3880#3969 这一系列 PR,我们从 Hexo 中去除了 Lodash。我们也在 You-Dont-Need-Lodash-Underscore 提交了 PR、将我们的 _.assignIn 的 Native 替代方案带给社区。

缓存实用函数的返回值

Hexo 的 hexo-util 中有许多的实用函数,如计算相对路径的 relative_url(from, to)、补全相对路径为 URL 的 url_for(path)full_url_for(path)、从 EMail 计算 gravatar URL 的 gravatar(mail)、判断一个链接是否为外部链接的 isExternalLink(url)。在 测试 中我们发现在 Hexo 生成过程中这些函数可能会被调用上千次,但在大部分调用中传入的参数都是相同的,因此可以把传入的参数和函数的返回值的键值对缓存起来。这一构想在 hexo-util#162 中被实现。

未来

#3776 中我们将 Benchmark 作为单元测试的一部分添加到 CI。之后 Benchmark 多次帮助我们找到潜在的性能问题(#3807#3833),避免了 #3129 那样造成严重的性能下降的情况再次发生。而在 #4000 中我们计划进一步将生成火焰图也作为单元测试的一部分,帮助我们继续优化 Hexo 的生成过程。对于 Hexo 来说,速度一直都是关键。

本文作者 : Sukka
本文采用 CC BY-NC-SA 4.0 许可协议。转载和引用时请注意遵守协议!
本文链接 : https://blog.skk.moe/post/how-we-make-hexo-4-2-faster/

本文最后更新于 天前,文中所描述的信息可能已发生改变