Repository navigation
perf: 向量语义搜索性能优化与召回率基准测试 - #412
Merged
Merged
Conversation
1.动态padding按batch最长对齐到8倍数(省30~70%推理时间) 2.PLINQ并行tokenize(batch>=8) 3.TensorPrimitives替代手写Vector循环(AVX2/AVX512) 4.ArrayPool复用long[]缓冲区+显式清零(降GC) 5.预构建NamedOnnxValue数组+提取dims 6.融合pooling除法1/(maskSum*norm)一次缩放 7.seqLenDim步长断言防错位 决策: 按收益优先级依次实施,全部编译通过+下游CodeIndex兼容
EmbedBatch返回扁平float[](batchSize*dim连续存储),省batchSize个float[]分配 mean pooling 0-GC:直接写入结果span,零临时分配 修正优化6数学bug:mean+L2等价v_sum/||v_sum||,maskSum被约掉,缩放1/norm非1/(maskSum*norm) SIMD maskSum:Vector<long>批量求和attention mask 标量fallback除法改乘法:1/norm一次缩放 IEmbeddingModel.EmbedBatchAsync→Task<float[]>(扁平) IAnnSearch.Add→ReadOnlySpan<float> 全链路适配:OnnxEmbeddingClient/EmbeddingIndex/BruteForceAnn/HnswAnn/FakeEmbeddingModel
maxLen手动循环替代LINQ Max(省lambda分配) 填充阶段用CollectionsMarshal.AsSpan从List<int>读取(连续内存无边界检查) 限制: BertTokenizer API无Span<int>输出重载,内部List<int>分配无法消除
验证点: mean pooling+L2归一化数学正确性, 扁平化向量索引, SIMD maskSum一致性, 动态padding, 返回长度, 确定性, 区分性, L2归一化 覆盖率: OnnxEmbedder类行覆盖88.19%, 核心方法MeanPoolSimd/SumMaskSimd/MeanPoolScalar 100%覆盖 28个测试全部通过(18纯数值+10端到端)
…/总计计时缺 Phase C(读+哈希)/F(删除)/G(失效缓存),补全后全链路可观测
新增12个测试: Dispose后异常/幂等/Dimensions缓存, batchSize>=8并行路径一致性, MeanPoolScalar多batch边界, SIMD与标量多batch等价 覆盖率提升: OnnxEmbedder类行覆盖88.19%->91.3%, 分支覆盖72.85%->77.14%, Dispose 0%->100% 40个测试全部通过(28原有+12新增)
无breaking changes,40测试全通过,0警告0错误 2.0.0仍无Span<int>输出重载,tokenizer内部List<int>分配仍是API限制
…arser | 决策: options with {Node=child} 每递归创建新 record,改直接传参; 98 ranges 各创建 parser/extractor 改 16 线程各一套工作窃取复用
…s 合并成 CollectContextUnified, CollectCalls+CollectDependencies 合并成 CollectCallsAndDeps, 节点类型不重叠可安全合并
…dingIndex 改用 InMemoryFileSystem 与 CodeIndexer 一致,OnnxEmbeddingClient 仍用 PhysicalFileSystem 读模型
- FindCallerFqn: O(n) 线性扫描 → O(log n) 二分查找(methodLookup 按 StartLine 排序) - BuildMethodLookup: LINQ Where+Select+ToList → foreach+Sort - SymbolIndex: ILookup ToLookup → Dictionary<string,List> 单遍历构建 - ExtractTypeUsageDependencies: LINQ Select+AddRange → foreach - FindChildByType(DepExtractor): LINQ FirstOrDefault → foreach
- 新增 SymbolStructures record 封装全部符号结构 - 新增 BuildSymbolStructures 单 foreach 构建: ClassNameSet/InterfaceNameSet/MethodLookup/ExtensionMethodMap/SymbolIndex/TypeFqnMap - SymbolIndex 新增内部构造器接受预构建字典 - ExtractAllCore: 6 次独立 BuildXxx 遍历 → 1 次联合遍历
- CollectTypeReferences 递归遍历每个类型声明子树 → 内联到外层联合遍历 - 新增 ProcessTypeReferenceNode: 单节点类型引用收集(不递归) - 新增 TryGetTypeDeclSourceFqn: 计算类型声明 source FQN 供上下文跟踪 - 新增 BuildTypeUsageEdges: 遍历后从累积 usedTypes 创建 DependencyEdge - CollectCallsAndDeps: 跟踪 currentTypeFqn 上下文 + 内联类型引用收集 - 移除 ExtractTypeUsageDependencies + CollectTypeReferences(已合并) - 3 次 AST 遍历 → 2 次(CollectSymbols + CollectContextUnified + CollectCallsAndDeps[含类型引用])
新建 SemanticTestCaseRepository(100题) + SemanticEvaluationEngine(MRR/HitRate) + SemanticSearchEvaluationTests 结果:P95=33ms 通过,HitRate@10=11% 远低于30%阈值 根因:500行固定块稀释函数名信号 + Async后缀导致语义偏向测试文件
- CollectSymbols 内联 ProcessVariableTypeNodeUnfiltered + ProcessInterfaceNameNode - 新增 ProcessVariableTypeNodeUnfiltered: 收集所有参数/变量类型(不过滤 classNameSet) - ExtractAllCore: CollectSymbols 后过滤 variableTypeMap(首字母大写或在 classNameSet 中) - 移除 CollectContextUnified(已合并到 CollectSymbols) - 3 次 AST 遍历 → 2 次(CollectSymbols[含变量类型+接口名] + CollectCallsAndDeps[含类型引用])
- Node.Type 是计算属性: 每次访问 P/Invoke + Marshal.PtrToStringAnsi 分配新 string - CollectCallsAndDeps: 16 次 node.Type 访问 → 1 次缓存 - CollectSymbols: 缓存 nodeType 传给 ProcessVariableTypeNodeUnfiltered/ProcessInterfaceNameNode/TryExtractSymbol - ProcessCallNode/ProcessDepNode/ProcessTypeReferenceNode: 接受 nodeType 参数 - ProcessVariableTypeNode/Unfiltered: 接受 nodeType 参数 - ProcessInterfaceNameNode: 接受 nodeType 参数 - TryExtractSymbol: 接受 nodeType 参数
- 新增 TreeSitterFieldIds 静态类: 从 Language 预计算 6 个 field ID - 29 个 GetChildForField(string) → GetChildForField(ushort) - 每次调用省: Encoding.UTF8.GetBytes 分配 + P/Invoke 字符串编码
- CollectCallsAndDeps/CollectSymbols/CollectCompilationUnitChildren: foreach → for+缓存Count - NamedChildCollection.GetEnumerator 每次迭代重新 P/Invoke Count → 缓存到局部变量 - yield return state machine 分配 → 消除
HitRate 12%→41%, MRR 0.068→0.225 CodeIndexHyperParams 集中存放 14 个超参数,支持环境变量覆盖 OnnxEmbeddingClient 构造函数接受 maxSeqLen 参数,移除环境变量读取
…dexHyperParams 读取
…h 返回扁平 float[] [batchSize×Dimensions],测试用 AsSpan(i*dim,dim) 切片,CosineSimilarity 改接受 ReadOnlySpan<float>
…用mmap,ONNX首次加载自动转ORT格式,N个InferenceSession共享同一mmap映射省(N-1)×22MB内存; 修复4个测试embedBatchSize哨兵守卫遗漏
…ge 后跟裸 squash/merge/rebase 时自动消费设 merge_method,并给 gh_pr_create 加 squash/merge/rebase 别名,提取 BindBooleanParam 扁平化避免嵌套超标
… anonymous、依赖提取返回空 | 决策: ExtractSymbolsCore 和 ExtractDependencies 都加 Initialize 纵深防御; CollectDependencies 补 ProcessTypeReferenceNode+BuildTypeUsageEdges 收集 Uses 边(之前合并到联合遍历后独立入口遗漏)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
本 PR 聚焦向量语义搜索(CSharpSymbolExtractor + OnnxEmbedder + EmbeddingIndex)的性能优化与召回率量化评估,共 23 个提交,42 文件变更(+1871/-347)。
性能优化
OnnxEmbedder 向量化全链路优化
AST 遍历合并(5→2 次)
TreeSitter P/Invoke 优化(消除 N+1 调用)
符号索引优化
召回率基准测试
超参数统一
可观测性
Bug 修复