性能与基准测试
分词性能相关的基准测试放在独立模块 znlp-benchmarks,用 JMH 实现,不会被 ./gradlew :znlp:test 触发。
# 默认跑 dev.znlp.benchmark.segment 包下的全部基准./gradlew :znlp-benchmarks:jmh
# 只跑某一个基准(正则匹配方法名)./gradlew :znlp-benchmarks:jmh -PjmhArgs='SegmentBenchmark.defaultSegment$'jmh 任务固定附加以下参数:
-rf json -rff build/results/jmh.json -foe true即结果以 JSON 输出到 znlp-benchmarks/build/results/jmh.json,-foe true 表示某个基准失败时继续跑完其余的。任务堆上限 2 GB。
| 项 | 值 |
|---|---|
| JMH 版本 | 1.37(jmh-core + jmh-generator-annprocess) |
| sourceSet | 独立的 jmh sourceSet,不在 main / test 里 |
| 运行时依赖 | :znlp + :znlp-resources + log4j |
SegmentBenchmark
Section titled “SegmentBenchmark”segment.benchmark.dev.znlp.SegmentBenchmark 是目前唯一的基准类。
| 配置 | 值 |
|---|---|
@BenchmarkMode |
AverageTime |
@OutputTimeUnit |
MICROSECONDS |
| Warmup | 2 轮 × 500 ms |
| Measurement | 5 轮 × 500 ms |
| Fork | 1 |
@Param |
{"short", "typical", "long"} 三档文本长度 |
结果单位是 μs/op(越小越好)。
6 个基准项
Section titled “6 个基准项”| 方法 | 测什么 |
|---|---|
defaultSegment |
默认分词器(词典 + 二元 + 人名 + POS) |
bigramOnlySegment |
只有词典和二元语法,不含人名和词性 |
cwsPipelineSegment |
感知机分词走完整流水线 |
cwsPerceptronSplit |
感知机直接 split,不走流水线 |
defaultSegmentToStrings |
默认分词 + segmentToStrings(只取词面) |
defaultSegmentConsumer |
默认分词 + Consumer<Token> 流式版本 |
后两项专门用于量化输出方式的开销:segmentToStrings 要构造 List<String>,流式版本不构造中间集合。对比 defaultSegment 和 defaultSegmentConsumer 可以看出收集成列表的成本。
bigramOnlySegment 与 defaultSegment 的差值就是人名识别 + 词性标注的开销;cwsPerceptronSplit 与 cwsPipelineSegment 的差值是流水线其余阶段的开销。这几组对比是选型的主要依据。
语料由 SegmentCorpus.text(size) 内置生成,不读外部文件。所以基准结果是自包含、可复现的,不依赖 data/ 或外部数据集。
仓库里没有已记录的基准数字
Section titled “仓库里没有已记录的基准数字”docs/设计架构/基准.md 只写了方法论和命令,并明确指出:
- JSON 结果不提交到仓库
- 不要把基准数字当作 PR 的硬门禁
仓库中 grep 不到任何 us/op 或 ops/s 形式的结果表。因此本站不提供性能数字——需要数据请自己跑:
./gradlew :znlp-benchmarks:jmhcat znlp-benchmarks/build/results/jmh.json性能优化方向
Section titled “性能优化方向”基于基准项的设计,常见的取舍是:
| 需求 | 选择 |
|---|---|
| 最高吞吐,不需要词性 | SegmentRecipe.dictionary() |
| 通用,需要词性 | SegmentRecipe.standard()(默认) |
| 需要地名/机构 | standard().withNer(),注意会强制带上 POS |
| 统计模型分词 | SegmentRecipe.perceptron() |
| 大文本 | segment(char[], Consumer<Token>),避免构造 Token 列表 |
| 长驻服务 | 启动时 build() 一次,单例复用(PipelineSegmenter 线程安全) |
List<Token> segment(String) 的建议输入上限是 10000 字符,超过请用流式接口。
- 设计文档:
docs/设计架构/基准.md - 配方对比:中文分词
- 容器内存与启动预热:Spring Boot 集成