Skip to content

perf: reduce Go FFI allocations and optimize native FTS ingestion / 优化 Go FFI 分配与原生全文索引写入 - #14

Draft
sunhailin-Leo wants to merge 8 commits into
zvec-ai:mainfrom
sunhailin-Leo:codex/optimize-go-ffi-upstream
Draft

sunhailin-Leo wants to merge 8 commits into
zvec-ai:mainfrom
sunhailin-Leo:codex/optimize-go-ffi-upstream

Conversation

@sunhailin-Leo

@sunhailin-Leo sunhailin-Leo commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

改动 / Changes

  • 中文:新增原生 FTS 索引优化:子模块 zvec 从 v0.7.0 提升至 fork 分支 perf/fts-store-positions-option、commit 2070ce7(基于 v0.7.0,仅改 gitlink,不修改 .gitmodules)。FTS extra_params 新增索引级开关 {"store_positions": false}:插入时跳过每个 (term, doc) 的位置列表($POS)写放,短语查询改为显式 InvalidArgument 报错(不再静默返回错误结果);非布尔值在 open() 校验失败;insert 复用 (term, doc_id) 键与 tf 缓冲减少临时分配。无新增 C 符号,老预编译库向前兼容(忽略该键)。同时新增 Go 集成测试与 FTS NoPos 基准变体。原生改动建议同步作为独立 PR 提交上游 zvec,合并后把 gitlink 重指上游 commit。
  • English: New native FTS optimization: bump the zvec submodule gitlink from v0.7.0 to fork branch perf/fts-store-positions-option, commit 2070ce7 (based on v0.7.0; only the gitlink changes, .gitmodules is left untouched). The FTS extra_params gains an index-level switch {"store_positions": false}: ingestion skips per-(term, doc) position-list ($POS) writes, phrase queries are rejected with an explicit InvalidArgument error instead of silently returning wrong results; non-boolean values fail loudly at open(); insert() reuses the (term, doc_id) key and tf buffers to cut per-term temporary allocations. No new C symbols — older prebuilt libraries ignore the key. Go integration tests and FTS NoPos benchmark variants are added. The native change is also proposed upstream for zvec; once merged, repoint the gitlink to the upstream commit.
  • 中文:cgo Query/MultiQuery/Fetch 连续包装结果并释放结果指针数组(含空/错误路径);批量 Delete/Fetch 共用 C 主键缓冲;字段 getter 减少堆分配,新增可复用缓冲的 GetVectorFP32FieldInto。purego 连续包装结果并合并字符串数组缓冲。新增 Darwin 原生堆回归测试。
  • English: Pack cgo Query/MultiQuery/Fetch results into contiguous Go storage and free result-pointer arrays, including empty/error paths; batch Delete/Fetch C keys; reduce field-getter allocations and add reusable-buffer GetVectorFP32FieldInto. Pack purego results and string arrays. Add Darwin native-heap regression tests.

数据 / Measurements

Apple M3 Pro · Go 1.24.3 · zvec v0.7.0. Getter figures are three-run medians; Go B/op excludes native allocations. Comparisons are case-specific (Query: main → PR; other rows: incremental).

场景 / Case Before → After 分配 / Allocations
cgo Query, 128D/TopK100 127.6 → 126.6 µs/op 103 → 4 allocs/op
cgo FP32 Into, 128D 176.7 → 154.8 ns/op 2 → 0 allocs/op
cgo string getter 191.6 → 159.8 ns/op 3 → 1 allocs/op
purego Fetch, 1,000 unique keys 273,865 → 249,289 Go B/op 2,011 → 2,010 allocs/op
  • 中文:1,000 键 Delete 的 C 字符串分配由逐键降至每批一次;Delete、purego Fetch 未证实整体耗时提速。原生堆负对照(各 20,000 次 × 10 个结果):故意禁用释放时 Query/Fetch 增长 1.68/1.70 MB,正常释放时最多增长 0.175/0.098 MB;这不是 main → PR 性能比较。
  • English: Batch Delete cuts C-string allocations from one per key to one per batch; no end-to-end latency gain is established for Delete or purego Fetch. Native-heap negative control (20,000 calls × 10 results each): deliberately disabling cleanup grows Query/Fetch by 1.68/1.70 MB, versus at most 0.175/0.098 MB with cleanup; this is not a main-vs-PR performance comparison.

全文索引 / FTS(原生优化 / native optimization)

本节为此 PR 的原生 FTS 插入速度与索引尺寸优化,来自另一台 Mac(搭载 cgo source 模式自建库、高后台负载),请勿与上表 M3 Pro 数据直接对比;组内对照组见下方负对照。180,224 文档 · varied 语料 · Batch512 · 每行一组单次运行。本 PR 修改了原生 FTS 实现(store_positions 开关)。

新增原生改动:FTS extra_params 索引级开关 {"store_positions": false} —— 插入跳过 (term, doc) 位置列表写放;短语查询显式报错(InvalidArgument);term/BM25 查询不受影响;无新增 C 符号。

指标 / Metric v0.7.0 基线(n=1) 本 PR store_positions:false(n=3) 变化 / Delta
累计插入 / Sum of Insert() 167.4 s 129.4 / 148.5 / 113.9 s(最快 129.4 s) -22.7%(129.4 vs 167.4)
关闭后目录 / Directory after Close 607.5 MB 302.5 MB(字节级再现) -50.1%
关闭后 FTS rocksdb 目录 / FTS rocksdb dir after Close 398.0 MB 93.3 MB -76.5%
Flush 后目录 / Directory after Flush 1,116.9 MB 706.4–719.0 MB -36.7%
  • 中文:负对照:同一 JSON 键在未修改的 v0.7.0 库上被忽略 → 167.4 s / 607.3 MB(与不开关位的 FTS 基线 160.0 s / 607.2 MB 在负载噪声内一致),证明提速与瘦身来自原生改动而非参数透传。开启 positions 的默认路径 after Close 目录尺寸不变(606.7 MB),磁盘输出逐字节等价;其 insert 时间在负载噪声范围内未见回归(数据不足下不声明微优化收益)。
  • English: Negative control: the same JSON key on the unmodified v0.7.0 library is ignored → 167.4 s / 607.3 MB (consistent with the positions-on baseline 160.0 s / 607.2 MB within load noise), so the gains come from the native change, not from the flag merely passing through Go. The default positions-on path keeps its after-Close directory size (606.7 MB) and byte-identical on-disk output; its insert time shows no regression beyond load noise, but no micro-optimization gain is claimed either (insufficient data).
  • 行为 / Behavior:store_positions:false 时 term/BM25 查询照常工作;短语查询被显式拒绝并且错误在 Close/重开之后保持;非布尔取值在 open() 校验失败(防止拼写错误静默切换)。原生 fts 单测 74/74 通过(新增 5 个 store_positions 用例:跳写 $POS 且 $TF 保留、短语拒答、密封后 term 查询、缺省路径回归、非法取值报错)。
  • 复现 / Reproduce: ZVEC_BENCH_FTS_ROWS=180224 ZVEC_BENCH_FTS_CORPUS=varied go test -tags integration -run '^$' -bench '^BenchmarkFTSIngestion/(FTS_Batch512|FTS_NoPos_Batch512)$' -benchtime=1x -count=1 -benchmem .(FTS_Batch512 两次运行取基线与默认路径验证;FTS_NoPos_Batch512 -count=3)

验证 / Validation

  • 中文 / English: cgo + purego integration suites, both go vet modes, cgo cgocheck2, formatting, and FTS reopen-query pass on macOS/arm64 against the rebuilt source-mode library. 本轮新增:zvec 原生 fts 列索引单测 74/74(含 5 个 store_positions 新用例);cgo source 全量套件连续 3 次通过;purego 套件通过(ZVEC_LIBRARY_PATH 指向重建库);race 模式下全套件存在一个既有的临界门限:TestCollectionQueryFetchNativeHeapDoesNotGrowPerResult(1 MB 预算)在本机多次测量为 0.65–1.18 MB——父分支与未修改的 v0.7.0 库同样波动并可越线(该门限只覆盖 invert+HNSW 查询路径,与本次 FTS 改动无交集;单独运行通过,CI 专用 runner 上 race 全套通过)。
  • 中文:CI 8/8 全绿(head 13bc090,.gitmodules 未改动指向 alibaba/zvec,GitHub fork 网络共享对象存储故按 SHA 可拉取 2070ce7;四平台 Source 从实库构建,含 race、purego、vet、benchmarks、gofmt,构建日志正确识别版本 v0.7.0-1-g2070ce7)。
  • English: CI is fully green (8/8) on head 13bc090 — .gitmodules untouched at alibaba/zvec; the pinned SHA 2070ce7 is fetchable through GitHub's fork-network object sharing. All four Source platforms build the real library and pass race, purego, vet, benchmarks, and gofmt; the build log reports version v0.7.0-1-g2070ce7 correctly.

@sunhailin-Leo sunhailin-Leo changed the title perf: reduce Go FFI hot-path allocations / 优化 Go FFI 热路径 perf: reduce Go FFI allocations and profile FTS ingestion / 优化 Go FFI 分配与全文索引剖析 Sep 24, 2026
@sunhailin-Leo sunhailin-Leo changed the title perf: reduce Go FFI allocations and profile FTS ingestion / 优化 Go FFI 分配与全文索引剖析 perf: reduce Go FFI allocations and optimize native FTS ingestion / 优化 Go FFI 分配与原生全文索引写入 Sep 24, 2026
@sunhailin-Leo
sunhailin-Leo force-pushed the codex/optimize-go-ffi-upstream branch 3 times, most recently from e604861 to 477b18d Compare September 24, 2026 18:55
LeoSun and others added 2 commits September 25, 2026 10:36
…marks

Expose the zvec FTS index-level extra_params switch
{"store_positions": false} to Go users via the existing
NewFTSIndexParams extraParams argument (no new C symbols, so older
prebuilt libraries keep working).

- New integration tests: phrase queries keep working with the default
  (positions on); with the flag off, term/BM25 queries keep working,
  phrase queries are rejected, and the behavior survives Close/Open.
- FTS ingestion benchmark gains NoPos variants (Batch1/128/512/After)
  so insert-time and on-disk gains/losses are measurable per corpus.

Co-Authored-By: Claude <noreply@anthropic.com>
Bump the zvec submodule from v0.7.0 to 2070ce7 (fork branch
perf/fts-store-positions-option, based on v0.7.0) which adds the
native FTS extra_params switch {"store_positions": false}:
skip per-(term, doc) position writes to cut ingestion cost and index
size, and reject phrase queries with an explicit error.

.gitmodules is intentionally left pointing at alibaba/zvec: the native
change is proposed upstream separately, and after it merges there the
gitlink should be repointed to the upstream commit. Until then the
pinned SHA exists only in the sunhailin-Leo/zvec fork, so source-mode
CI cannot fetch it at checkout (expected red Source jobs); validation
runs locally from the fork checkout instead.

Measured on 180,224 docs / varied corpus (macOS arm64, source mode):
sum of Insert() 167.4s -> 129.4s (-22.7%); closed collection total
607.5 MB -> 302.5 MB (-50.1%); closed FTS rocksdb dir 398.0 MB ->
93.3 MB (-76.5%). Positions-on ingestion keeps identical on-disk
output.

Co-Authored-By: Claude <noreply@anthropic.com>
@sunhailin-Leo
sunhailin-Leo force-pushed the codex/optimize-go-ffi-upstream branch 2 times, most recently from 0cee138 to 13bc090 Compare September 25, 2026 03:55
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.

1 participant