[PERF] Quadratic (O(N^2)) serialization time for large BOMs — `Bom.validate()` → `register_dependency()` linear scan
评估
- 难度
- 3/5
- 预计耗时
- 1-2 天
- 新手友好度
- 36/100
- Issue 类型
- 缺陷
- 描述清晰度
- 描述清楚
- 活跃度
- 停滞
- 技术栈
- python
- 领域
- performance
调研方向
从 cyclonedx/model/bom.py 中的 Bom.validate() 和 register_dependency() 开始,然后检查 cyclonedx/output/json.py 以确认序列化路径。审查链接的 pull request #1007,并添加或运行一个回归/扩展性测试,以证明其行为接近线性,同时保持逐字节完全相同的输出。
由索引模型根据 Issue 内容生成。
描述
Environment
cyclonedx-python-lib9.1.0 (the offending code path is unchanged onmain/ 11.x)- Python 3.10, Linux
Description
Serializing a BOM with many components scales quadratically with the number of components, not linearly. For a container SBOM with several thousand components, output_as_string() stalls for minutes, almost entirely inside Bom.validate().
Steps to reproduce
import time
from cyclonedx.model.bom import Bom
from cyclonedx.model.component import Component, ComponentType
from cyclonedx.output import make_outputter
from cyclonedx.schema import OutputFormat, SchemaVersion
for N in (1000, 2000, 4000, 8000):
bom = Bom()
bom.metadata.component = Component(name="root", type=ComponentType.CONTAINER, bom_ref="root")
for i in range(N):
bom.components.add(Component(name=f"c{i}", version="1.0",
type=ComponentType.LIBRARY, bom_ref=f"ref-{i}"))
out = make_outputter(bom=bom, output_format=OutputFormat.JSON,
schema_version=SchemaVersion.V1_6)
t = time.perf_counter(); out.output_as_string()
print(f"N={N}: {time.perf_counter()-t:.2f}s")
Expected vs actual
Expected: time grows roughly linearly with N.
Actual: time grows roughly 4x per 2x N (quadratic):
N=1000: 0.14s
N=2000: 0.48s (x3.4)
N=4000: 1.75s (x3.6)
N=8000: 6.70s (x3.8)
A cProfile run at N=15000 shows ~112M calls to the lambda at cyclonedx/model/bom.py:653.
Root cause
cyclonedx/output/json.py—generate()unconditionally callsbom.validate()during serialization (there is no opt-out).cyclonedx/model/bom.py—Bom.validate()callsself.register_dependency(target=...)once per component (and per service).cyclonedx/model/bom.py—register_dependency()locates the existing entry with a linear scan:
Called once per component over the growing dependency collection, this is O(N²) overall._d = next(filter(lambda _d: _d.ref == target.bom_ref, self.dependencies), None)
Proposed fix
Replace the linear lookup with an indexed (dict[ref -> Dependency]) lookup. In a local benchmark an indexed variant produces byte-identical output and gives ~8x speedup at N=6000 (the gap widens with N). I'm happy to open a PR with the fix and a regression/scaling test.
- 主要语言
- Python
- 星标
- 117
- 派生
- 67
- 平均合并
- 21 小时 9 分钟
- 30 天内合并 PR
- 3
环境准备
- 没有 Dockerfile 或 Docker Compose 文件
- 有 Pull Request 模板
- 阅读贡献指南
从这里开始
- 先读完整个 Issue,再读项目的贡献指南。
- 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
- Fork 仓库,在一个分支上完成修改。
- 提交 Pull Request,并在描述里引用这个 Issue 编号。
CycloneDX/cyclonedx-python-lib 的其他 Issue
-
tests: test all model enums可能重新可做 @jkowalleck 于 126 天前认领,目前没有进行中的 PR。 未关闭QA
CycloneDX/cyclonedx-python-lib#991 · 已指派 1 人 ·
-
feat(deps)!: make all de/serialization libraries optional可能重新可做 @Simoh23999 于 73 天前认领,目前没有进行中的 PR。 未关闭breaking change dependencies
难度 5/5 一周以上 新手友好度 35/100
CycloneDX/cyclonedx-python-lib#979 · 2 条评论 ·
-
feat: Add support for Component signature可能已有人在做 @wiebe-vandendriessche 于 124 天前认领。 未关闭enhancement help wanted schema 1.4
难度 3/5 1-2 天 新手友好度 58/100
CycloneDX/cyclonedx-python-lib#978 · 4 条评论 ·
-
chore: have coverage uploaded consitently可能重新可做 @jkowalleck 于 172 天前认领,目前没有进行中的 PR。 未关闭chore
CycloneDX/cyclonedx-python-lib#966 · 已指派 1 人 ·
-
chore: use dependabot cooldown period可能重新可做 @jkowalleck 于 174 天前认领,目前没有进行中的 PR。 未关闭chore
CycloneDX/cyclonedx-python-lib#965 · 1 条评论 · 已指派 1 人 ·
查看 CycloneDX/cyclonedx-python-lib 的全部 Issue
相似的 Issue
-
难度 2/5 1-3 小时 新手友好度 68/100
KalicoCrew/kalico#1012 ·
维护者通常 1 天内回复
-
难度 1/5 1 小时以内 新手友好度 82/100
esphome/esphome.io#7657 ·
维护者通常 1 天内回复
-
难度 2/5 1-3 小时 新手友好度 62/100
VNG-Realisatie/gemma-zaken#2658 ·
-
难度 1/5 1 小时以内 新手友好度 75/100
维护者通常 1 天内回复
-
comp/gateway comp/plugins duplicate P3 type/bug
难度 2/5 1-3 小时 新手友好度 82/100
NousResearch/hermes-agent#137019 ·
维护者通常 1 天内回复