Gatsby Gabe 基准测试指南:gabe-fs-markdown-images 的图像池与 Markdown 构建性能测量
【免费下载链接】gatsbyReact-based framework with performance, scalability, and security built in.项目地址: https://gitcode.com/gh_mirrors/ga/gatsby
Gatsby 官方仓库的 Gabe 基准测试(benchmark)项目用于量化 Gatsby 在"文件系统(fs)+ Markdown"数据源形态下的构建性能基线。本文聚焦gabe-fs-markdown-images这一基准站点:它在纯 Markdown 之外,为每篇文章额外附带一张 JPG 图片(图片不内嵌于 Markdown 正文,而是通过 frontmatter 字段引用),从而量化"带图片的 Markdown 站点"与"纯 Markdown 站点"之间的构建性能差异。读完本文,你将掌握该基准站点的安装方式、图像池的预生成与多线程并行生成技巧、基准运行命令的参数语义,以及底层生成脚本gen.js的实现原理。
基准测试的背景与定位
gabe-fs-markdown-images是 benchmarks 目录下 Gabe 系列基准站点之一,属于"baseline(基线)"类型。基线站点的目标不是模拟真实业务复杂度,而是用大量结构极其简单、内容完全一致的页面,把构建管线中某个特定环节的耗时放大出来。
该站点要追踪的指标是:每个页面对应一个独立的 Markdown 文件,且每篇文章额外附带一张图片(图片不属于 Markdown 内容本身)时的整体构建性能。其页面内容刻意保持极简——每页只有一个小标题、一段引言引用和两段随机文本,且图片不写入 Markdown 正文,因为基准的目的是测量 Markdown 解析与页面生成本身,而不是文本长度对性能的影响。
与 gabe-fs-markdown 的对照意义
gabe-fs-markdown-images的结果应与其姊妹站点 gabe-fs-markdown 的结果进行对比:后者是同样的文件系统 + Markdown 结构,但完全不含图片。两者的差异即"在 Markdown 页面中引入图片(图片处理管线)"所带来的近似性能影响(tentative impact)。之所以说是"近似",因为两个站点的随机内容是独立生成的,无法做到逐字节一致,但统计意义上足够反映图片处理(Sharp 图像优化、GatsbyImage 生成等)带来的开销增量。
从依赖清单 package.json 可以看到,图片处理链路由gatsby-plugin-image、gatsby-plugin-sharp、gatsby-transformer-sharp三个插件组成,这也是生产站点中处理本地图片的典型组合。
安装与依赖
与大多数 Gabe 基准站点一致,安装只需:
yarn # 或 npm install站点运行于 Node.js 环境,主要依赖包括gatsby、gatsby-transformer-remark(Markdown 解析)、gatsby-source-filesystem(读取生成的文章与图片目录)、gatsby-plugin-image/gatsby-plugin-sharp/gatsby-transformer-sharp(图片处理),以及生成随机内容用的faker、生成随机噪点 JPG 的js-image-generator、进度条progress和清理目录的rimraf。
运行时环境要求:Node 版本与 worker_threads
图片批量生成依赖 Node.js 的worker_threads模块实现多线程并行。该模块从Node 10.15起才可用(README 中写为 10.13,源码 gen.js 的注释与警告信息则明确为 10.15)。如果你在更老的 Node 版本上设置了C(worker 数量),脚本会打印警告并退化为单线程模式继续执行,因此图片生成会明显变慢,但不会报错终止。
图像池(Image Pool):先预生成,再拷贝复用
与 Gabe 系列其他站点不同,gabe-fs-markdown-images的生成逻辑更复杂,因为它要先构造图像池,再从池中把图片拷贝到目标位置。
为什么不能直接为每篇文章生成一张新图片?因为图片生成(js-image-generator逐像素生成随机噪点 JPG)极其昂贵:源码注释与 README 都明确指出,单线程生成默认尺寸下 128k 张图片需要约 2 小时,即使生成 1000 张默认尺寸图片也需要约 10 分钟。若每次运行基准都重新生成图片,基准本身将变得不可用。
解决方案是分层设计:
- 一次性预生成图像池:把将来可能用到的所有图片提前生成好,存放在
generated_image_pools/jpg/<宽>x<高>/目录下,以0.jpg、1.jpg、2.jpg… 顺序编号。 - 基准运行时只做拷贝:运行基准或生成随机内容时,先检查图像池数量是否足够;不够则增量补足。一旦池中某类型/尺寸的图片足够,就为每个生成的
.md文件从池中拷贝一张同名图片(fs.copyFileSync),拷贝速度远快于生成。
用 worker 并行预生成图像池
对于大规模页面数,推荐先一次性把所需数量的图片全部生成出来。图像池在多次基准运行之间持续保留,因此这笔成本只需支付一次:
C=8 W=100 H=100 N=128000即使用 8 个 worker 线程,生成 128k 张 100×100 的图片(实际执行需配合node gen.js或yarn bench等入口,见下文"运行基准")。注意:
- 若设置了
C,脚本会无视已有图片、强制全量重新生成,并把 N 张图片的生成任务切分给 C 个 worker(见 gen.js 的forceRegenerateAllWithWorkers)。 - 若未设置
C,脚本只做增量补充:读取池中现有文件数量count,假设编号 0..count-1 连续无缺口,仅从count生成到N-1(见incrementallyRegenerateNoWorkers,gen.js)。
源码中的 worker 切分逻辑为:step = floor(N / C),前 C-1 个 worker 各生成step张,最后一个 worker 生成剩余的lastStep = N - step * (C - 1)张;每个 worker 进程都会向主线程postMessage(1)驱动进度条推进。若池为空且N > 1000,脚本还会在控制台提示改用C=4 W=... H=... N=... node gen.js来多线程分摊工作量。
图像池的持久性与随机内容的非持久性
关键设计约束:图像池跨基准运行持久保留,而随机生成的站点内容不会。每次运行bench脚本都会先删除上一次的generated_articles与generated_images,因此文章内容是全新的随机数据,但图片池得以复用——这正是"一次生成、多次基准"成本模型的核心。
运行基准:命令与参数详解
无论图像池是否已存在,都可以直接启动一次基准运行;若池不存在或图片数量不足,脚本会自动生成(或增量补足)图片:
W=100 H=200 N=1000 M=2 yarn bench各参数语义如下:
| 参数 | 示例 | 含义 |
|---|---|---|
N=1000 | N=1000 | 构建包含 1000 个页面的站点(文章数量) |
M=2 | M=2 | 指示 Node.js 为长期存储(构建缓存)使用最多 2GB 内存 |
W=100 | W=100 | 使用 100px 宽的图片 |
H=200 | H=200 | 使用 200px 高的图片 |
C=8(可选) | C=8 | 强制按给定尺寸重新生成图像池,并用 8 个 worker 线程并行;每种图片尺寸只需执行一次 |
bench脚本的完整流程定义在 package.json 中:
rm -rf generated_articles generated_images; gatsby clean; N=${N:-512} node gen.js; CI=1 node --max_old_space_size=${M:-2}000 node_modules/.bin/gatsby build一次基准运行依次执行:
- 删除上次生成的产物(
generated_articles、generated_images); - 生成
N篇伪随机内容文章,每篇从图像池中拷贝一张图片(N未设置时默认512); - 执行
gatsby clean清空构建缓存; - 执行
gatsby build完成生产构建(M未设置时默认1GB堆内存上限,即--max_old_space_size=2000对应M=2)。
默认值一览
- 未设置
N:默认构建512个页面。 - 未设置
M:默认使用1GB内存。 - 源码层面(gen.js):文章数
N默认 100;图片宽W默认 640;高H默认 326;worker 数C默认 0(即增量单线程模式)。注意 README 示例中的N语义(页面数)与 gen.js 中的默认N略有差异——yarn bench通过N=${N:-512}把环境变量传入,因此以运行基准时的环境变量为准。
生成的内容形态与页面构建链路
随机文章的生成
gen.js的generateArticles(gen.js)为每篇文章:
- 用
faker.lorem.sentence()生成一个句子作为标题,并用faker.helpers.slugify(sentence).toLowerCase()生成 slug; - 写入
generated_articles/<slug>.md,frontmatter 包含articleNumber、title、description、slug、date,以及引用图片的关键字段rngImg: ../generated_images/<slug>.jpg; - 用
fs.copyFileSync把池中的i.jpg拷贝为generated_images/<slug>.jpg; - 正文即一级标题、引言引用和
faker.lorem.paragraphs(2)生成的两段正文。
可见图片与 Markdown 是两套独立文件:Markdown 仅通过 frontmatter 的rngImg相对路径指向图片,图片本身不出现在 Markdown 正文中,这正是本基准与"Markdown 内嵌图片"场景的区别点。
数据源与页面创建
gatsby-config.js 注册了两个gatsby-source-filesystem实例:blog指向generated_articles,img指向generated_images,再经由gatsby-transformer-remark解析 Markdown、gatsby-transformer-sharp处理图片节点。
gatsby-node.js 的createPages通过 GraphQL 查询allMarkdownRemark,为每篇文章创建一个页面,模板为src/templates/blog-post.js,并把id、slug、previous、next(用于上一篇/下一篇导航)放入 pageContext。
模板中的图片消费
页面模板 blog-post.js 的 GraphQL 查询把 frontmatter 中的rngImg解析为childImageSharp.gatsbyImageData,再通过gatsby-plugin-image的<GatsbyImage image={gatsbyImageData} alt="random stuff" />渲染图片。也就是说,每张池中图片在构建时都要经过 Sharp 的图像处理管线并生成优化的gatsbyImageData——这既是页面渲染所必需,也正是本基准希望计量的开销来源。首页 index.js 则只列出文章的标题、日期与描述,不加载图片。
使用建议与注意事项
- 大页面数优先预生成图像池:若要测 128k 量级的站点,务必先用
C=8 W=100 H=100 N=128000之类命令把池建好,避免基准运行时把 2 小时的单线程生成耗时混入测量结果。 C是强制重建开关:一旦设置C,同尺寸池会整体重做;只想增量补图时不要带C。- 区分环境变量默认值:
yarn bench的N默认 512、M默认 1GB;而gen.js内部N/W/H的默认值分别是 100/640/326,直接运行node gen.js与运行yarn bench的规模并不相同。 - Node 版本决定是否多线程:
worker_threads需要 Node ≥ 10.15,旧版本会收到警告并回退到单线程。 - 结果解读:将本基准的构建耗时与 gabe-fs-markdown 对比,即可近似得到"文件系统 + Markdown + 图片"相对"纯 Markdown"的性能增量;如需进一步隔离图片尺寸对耗时的影响,可固定
N、M,仅改变W/H分别运行多组实验。
如需深入理解生成逻辑,建议直接阅读 gen.js(图像池生成、worker 切分、文章生成)与 blog-post.js(图片的 GraphQL 查询与渲染),再结合yarn bench的构建输出与时间统计验证参数效果。
【免费下载链接】gatsbyReact-based framework with performance, scalability, and security built in.项目地址: https://gitcode.com/gh_mirrors/ga/gatsby
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考