Skip to content

perf(kvrec): 键索引修复逐键读取 O(n²)(fixes #24) - #25

Merged
EthanPeng-2048 merged 1 commit into
mainfrom
fix/kvrec_opti
Oct 25, 2026
Merged

EthanPeng-2048 merged 1 commit into
mainfrom
fix/kvrec_opti

Conversation

@EthanPeng-2048

Copy link
Copy Markdown
Collaborator

问题(#24)

KeyValueRecord::get/has 对 fields_ 做线性扫描。真实调用方(go.neuralnet.cpp 的 parse_nngames)是逐键读取整个 record,导致 O(n²) 次字符串比较:80k 键 33.7s,全量 .nngames parse 占 verify 总时长 51%。

修法(issue 建议方案 1:内存结构维护索引)

新增私有键索引 key_index_(key → fields_ 首次出现下标):

  • set() 四个重载 emplace 维护索引(emplace 保留首个出现下标)
  • parse()/parse_at() 填充 fields_ 后重建索引
  • has()/get() 只读查询 —— 并发 const 读取安全(无需 mutable 惰性构建)
  • 重复键 + 类型错位时从首个出现处前向兜底扫描,与旧版"返回首个 key+type 匹配"语义逐例一致
  • 嵌套 Record 的 get 重建子记录索引

容器字节布局零变化(.nngames 数据身份契约不受影响),只动内存读取路径。

验证

issue #24 中的微基准(clang -std=c++23 -O2):

N 修复前 修复后
10,000 0.283 s 0.001 s
20,000 0.675 s 0.003 s
40,000 4.152 s 0.006 s
80,000 33.668 s 0.014 s

耗时随 N 线性增长,二次复杂度消除。

现有测试全部通过:kvrec_test、dataset_samples_test、model_spec_validation_test、rl_output_test、train_state_test。

Fixes #24

KeyValueRecord::get/has linearly scanned fields_ on every call. Real
callers (e.g. go.neuralnet.cpp parse_nngames over a .nngames container)
do per-key reads over the whole record, yielding O(n²) string compares:
80k keys took 33.7s, and full-dataset parse was 51% of verify time.

Add a private key_index_ (key -> first-occurrence offset):
- set() overloads emplace into the index (first occurrence wins)
- parse()/parse_at() rebuild the index after populating fields_
- has()/get() are read-only lookups; concurrent const reads stay safe
- duplicate keys with type mismatch keep the old "first key+type match"
  semantics via forward fallback scan from the first occurrence
- nested Record get() rebuilds the sub-record's index

The container byte layout is unchanged (downstream data identity
contract); this only touches the in-memory read path.

Benchmark (issue #24, clang -std=c++23 -O2): 80k per-key gets
33.7s -> 0.014s; scaling now linear. Fixes #24
@EthanPeng-2048
EthanPeng-2048 merged commit 5c03956 into main Oct 25, 2026
3 checks passed
@EthanPeng-2048
EthanPeng-2048 deleted the fix/kvrec_opti branch October 28, 2026 13:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[性能] KeyValueRecord::get 线性扫描导致逐键读取 O(n²):80k 键 33.7s,全量 .nngames parse 占 verify 51%

1 participant