> For AI agents: the complete documentation index is available at https://renmori.mewbaka.cn/llms.txt, the full documentation bundle is available at https://renmori.mewbaka.cn/llms-full.txt.

# 性能基准

本页记录 RenMori 1.1.0 的性能基准结果：测试在真实的 Ren'Py 8.5.2 引擎中进行，以同一批资源分别构建未保护、基线保护与完整保护三种发行版构建，并通过引擎自身的资源加载路径 `renpy.loader.load` 读取全部资源后计时，因此测得的是实际发行的运行时开销，而非独立的加密库性能。

测试脚本为仓库中的 [`tools/benchmark.py`](https://github.com/MewBaka/RenMori/blob/main/tools/benchmark.py)，本页数据对应的原始结果见 [`docs/benchmarks/2026-10-31-apple-m1.json`](https://github.com/MewBaka/RenMori/blob/main/docs/benchmarks/2026-10-31-apple-m1.json)。

## 结论摘要

- 完整保护下，引擎内解密吞吐为 567 至 1529 MB/s，具体取决于单个文件的大小
- 读取一张 2 MB 的 CG，完整保护比未保护多耗时约 0.95 ms，作为参照，60 FPS 下的单帧时间为 16.7 ms
- 在视频中随机 seek 并读取 64 KB，完整保护的中位耗时为 0.086 ms
- 加载并解析全部脚本的 `lint` 耗时由 0.52 s 增加到 0.67 s
- 受保护构建的 `game/` 目录体积仅增加 0.18%
- 构建期保护 319 MB 资源，完整保护耗时约 13 s

## 测试环境

| 项目          | 取值                       |
| ----------- | ------------------------ |
| 测试日期        | 2026-10-31               |
| 设备          | Apple M1，8 GB 内存         |
| 操作系统        | macOS 26.5.1（arm64）      |
| Ren'Py      | 8.5.2.26010301           |
| RenMori     | 1.1.0，闭源插件 plugin-v1.1.0 |
| 构建工具 Python | 3.12.12                  |

## 测试方法

### 工作负载

以 Ren'Py SDK 示例项目 `the_question` 为基础，向 `game/` 中加入一组按商业视觉小说规模设计的合成资源，合计 812 个文件，加上示例项目自身后 `game/` 目录共 319.3 MB；合成资源的内容为随机字节，与已编码的图像、音频、视频一样不可压缩。

| 类别    | 扩展名     |  单文件大小 | 文件数 |       合计 |
| ----- | ------- | -----: | --: | -------: |
| CG    | `.png`  |   2 MB |  40 |  80.0 MB |
| 立绘    | `.png`  | 600 KB |  60 |  35.2 MB |
| UI 素材 | `.png`  |  24 KB | 400 |   9.4 MB |
| BGM   | `.ogg`  |   4 MB |  10 |  40.0 MB |
| 语音    | `.opus` |  60 KB | 300 |  17.6 MB |
| 视频    | `.webm` |  64 MB |   2 | 128.0 MB |

### 测量项目

| 项目       | 方法                                                                                 |
| -------- | ---------------------------------------------------------------------------------- |
| 引擎内读取吞吐  | 向构建中注入一个基准扩展，在引擎启动阶段通过 `renpy.loader.load` 以 1 MB 为单位读取每个文件；每个类别先预读一轮，再计时 5 轮，取中位数 |
| 视频流式读取延迟 | 对 64 MB 视频分别测量「打开并读取前 64 KB」与「随机 seek 后读取 64 KB」，各采样 200 次，取中位数                    |
| 脚本加载     | 对每种构建完整运行 `renpy.sh <game> lint` 3 次，取中位数；`lint` 会从容器中加载并解析全部脚本                    |
| 构建耗时     | 对同一输入运行 `renmori protect` 3 次，取中位数                                                 |
| 体积开销     | 受保护构建与未保护构建的 `game/` 目录体积之差，包含容器、加载器扩展与脚本变换的全部影响                                   |
| 分块大小     | 在构建工具的 Python 中，以参考读取器对 64 MB 数据分别测量 4 种分块大小下的顺序吞吐与随机 4 KB 读取延迟                    |

本页中的 MB 均指 2<sup>20</sup> 字节。

## 结果

### 引擎内读取吞吐

单位为 MB/s，数值越大越好。

| 类别    |  单文件大小 |  未保护 | 完整保护 | 基线保护 |
| ----- | -----: | ---: | ---: | ---: |
| CG    |   2 MB | 5590 | 1529 |  108 |
| 立绘    | 600 KB | 5585 | 1463 |  120 |
| UI 素材 |  24 KB |  729 |  567 |  100 |
| BGM   |   4 MB | 4608 | 1048 |  115 |
| 语音    |  60 KB | 1197 |  749 |   97 |
| 视频    |  64 MB | 3822 | 1287 |  112 |

### 单文件读取耗时

单位为 ms，表示读取一个完整文件所需的时间。

| 类别    |  单文件大小 |   未保护 |  完整保护 |   额外耗时 |
| ----- | -----: | ----: | ----: | -----: |
| CG    |   2 MB |  0.36 |  1.31 |  +0.95 |
| 立绘    | 600 KB |  0.11 |  0.40 |  +0.30 |
| UI 素材 |  24 KB | 0.032 | 0.041 | +0.009 |
| BGM   |   4 MB |  0.87 |  3.82 |  +2.95 |
| 语音    |  60 KB | 0.049 | 0.078 | +0.029 |

视频不以整个文件为单位读取，而是由引擎流式读取，因此以下一节的延迟数据为准。

### 视频流式读取延迟

单位为 ms，取中位数。

| 测量                |   未保护 |  完整保护 |  基线保护 |
| ----------------- | ----: | ----: | ----: |
| 打开并读取前 64 KB      | 0.051 | 0.082 | 0.596 |
| 随机 seek 后读取 64 KB | 0.035 | 0.086 | 1.113 |

### 脚本加载

| 构建   | `lint` 耗时 |
| ---- | --------: |
| 未保护  |    0.52 s |
| 完整保护 |    0.67 s |
| 基线保护 |    0.58 s |

`the_question` 包含 52 个已编译脚本，三种构建的差异均在 0.15 s 以内。

### 构建耗时与体积开销

| 保护强度 |      耗时 |       处理速率 | `game/` 体积变化 |
| ---- | ------: | ---------: | -----------: |
| 基线保护 |  2.52 s | 126.8 MB/s |      +0.147% |
| 完整保护 | 13.41 s |  23.8 MB/s |      +0.177% |

构建耗时只发生在开发者机器或 CI 上，不影响玩家；完整保护由闭源插件完成，包含逐构建的结构多态等额外处理，构建耗时高于基线保护。

### 分块大小

以下数据在构建工具的 Python 中通过 `cryptography` 后端测得，用于比较 `chunk_size` 配置的取舍，不代表引擎内的绝对性能。

|      分块大小 |      顺序吞吐 | 随机 4 KB 读取（中位） | 随机 4 KB 读取（p95） |   体积开销 |
| --------: | --------: | -------------: | --------------: | -----: |
|     16 KB | 1273 MB/s |       0.011 ms |        0.022 ms | 0.098% |
| 64 KB（默认） | 2008 MB/s |       0.024 ms |        0.050 ms | 0.025% |
|    256 KB | 2339 MB/s |       0.077 ms |        0.124 ms | 0.006% |
|      1 MB | 1461 MB/s |       0.286 ms |        0.482 ms | 0.002% |

分块越大，顺序吞吐越高、体积开销越小，但随机读取需要解密的数据也越多；默认的 64 KB 在两者之间取得平衡，一般无需调整。

## 结果解读

完整保护使用闭源插件提供的原生运行时，是发行版构建的默认选项；基线保护使用 Python 编写的参考读取器，吞吐约为完整保护的 1/14 至 1/6，仅适用于无法使用插件的场景，详见 [保护强度与闭源插件](https://renmori.mewbaka.cn/cli/plugin.md)。

上表中「未保护」的数据接近内存拷贝速度，原因是测试时资源已位于操作系统的页缓存中；这一设置排除了磁盘性能的差异，使结果只反映解密本身的 CPU 开销；在玩家机器上，首次读取通常受磁盘速度限制，受保护与未保护之间的相对差距会相应缩小。

## 局限

- 未测量实际游玩中的帧时间；本页数据只覆盖资源读取与解密，不包括图像解码、音频解码与渲染
- 合成资源为随机字节，引擎只读取而不解码这些文件
- 测试在页缓存热状态下进行，未包含冷启动时的磁盘 I/O
- 仅在一台 Apple M1 设备上测试，Windows、Linux 与 Steam Deck 上的数据尚未测量

## 复现

需要 Ren'Py SDK 与 RenMori 源码仓库；完整保护需要闭源插件，首次运行时会自动下载。

```bash
git clone https://github.com/MewBaka/RenMori.git
cd RenMori && pip install -e .
python tools/benchmark.py --sdk /path/to/renpy-8.5.2-sdk \
    --game /path/to/renpy-8.5.2-sdk/the_question --json result.json
```

| 参数           | 默认值    | 说明                                    |
| ------------ | ------ | ------------------------------------- |
| `--scale`    | 1.0    | 工作负载规模的倍数，1.0 约为 310 MB 合成资源          |
| `--passes`   | 5      | 引擎内每个类别的计时轮数                          |
| `--repeat`   | 3      | `protect` 与 `lint` 的重复次数              |
| `--strength` | `both` | 构建哪些受保护版本，可选 `both`、`baseline`、`full` |
| `--json`     | 无      | 将原始结果与汇总结果写入指定文件                      |
| `--keep`     | 关闭     | 保留工作目录，便于检查生成的构建                      |

不同设备上的绝对数值会有差异，比较时请以同一设备上三种构建之间的相对结果为准。
