Conversation
PR Reviewer Guide 🔍Here are some key observations to aid the review process:
|
|
Failed to generate code suggestions for PR |
qin-ctx
left a comment
There was a problem hiding this comment.
[Bug] .github/workflows/_build.yml — verify-macos-14-wheel-on-macos-15 job(第 428-475 行)仍然使用旧的 smoke test 逻辑,通过 glob 匹配 engine*.so 单文件。但本 PR 已将 engine 从单个扩展模块改为 Python package(engine/__init__.py + _native.so 等子模块),旧的 glob 模式无法匹配到新的文件布局。合并后该 CI 验证 job 会始终失败,报 openviking storage engine extension was not installed。请更新为新的 engine package import 方式。
src/CMakeLists.txt
Outdated
| target_link_libraries(${INDEX_TARGET} PUBLIC Threads::Threads) | ||
| if(OV_PLATFORM_ARM) | ||
| target_link_libraries(${INDEX_TARGET} PUBLIC krl) | ||
| endif() |
There was a problem hiding this comment.
[Bug] if(OV_PLATFORM_ARM) 出现在 if(OV_PLATFORM_X86) 的循环内部,但这两个平台标志是互斥的(基于 CMAKE_SYSTEM_PROCESSOR 的匹配),所以这段代码永远不会执行,是死代码。
更关键的问题:拆分后 engine_common(包含 store/*.cpp 和 common/*.cpp)没有链接 krl。原来的单体 engine_impl STATIC 库包含所有源文件并通过 PRIVATE 链接了 krl。如果 store/ 或 common/ 中有代码依赖 KRL 符号或头文件,在 ARM 平台上会出现链接错误。
建议:移除这个不可达的代码块,并确认 engine_common 在 ARM 上是否需要 target_link_libraries(engine_common PUBLIC krl)。
|
|
||
| #define OV_EXPAND_MACRO(name) name | ||
|
|
||
| PYBIND11_MODULE(OV_EXPAND_MACRO(OV_PY_MODULE_NAME), m) { |
There was a problem hiding this comment.
[Bug] OV_EXPAND_MACRO 是一个单层恒等宏,用于 PYBIND11_MODULE 内部会做 token 拼接(PyInit_##name)的场景。这种写法比较脆弱——如果 pybind11 内部宏结构发生变化,OV_PY_MODULE_NAME 可能在拼接前未被完全展开。
pybind11 >= 2.6 内部已经提供了一层宏间接展开,可以直接传入宏定义:
PYBIND11_MODULE(OV_PY_MODULE_NAME, m) {请用项目要求的最低 pybind11 版本(pyproject.toml 中为 2.13.0)验证这种写法是否可行。
| target_link_libraries(engine_impl INTERFACE engine_common engine_index_sse3 Threads::Threads) | ||
| if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") | ||
| if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS "9.0") | ||
| target_link_libraries(engine_impl INTERFACE stdc++fs) |
There was a problem hiding this comment.
[Suggestion] engine_impl INTERFACE 目标的 filesystem 库链接逻辑在 x86 分支(第 278-290 行)和非 x86 分支(第 307-319 行)中完全重复,与 ov_link_filesystem_libs() 函数的功能一致。建议复用该函数(适配 INTERFACE 目标)以消除重复代码。
| if variant in available and variant in supported_x86: | ||
| return variant, available, None | ||
|
|
||
| if "x86_sse3" in available: |
There was a problem hiding this comment.
[Suggestion] 这个 if "x86_sse3" in available fallback 看起来是冗余的。_X86_PRIORITY(第 20 行)已经包含 x86_sse3,而 _supported_x86_variants() 默认就包含 x86_sse3(第 43 行:supported = {"x86_sse3"})。所以如果 x86_sse3 在 available 中,它在上面的 for variant in _X86_PRIORITY 循环中就已经被选中了,不会走到这里。
|
|
||
| def build_extension(self, ext): | ||
| """Build a single Python native extension artifact using CMake.""" | ||
| if getattr(self, "_engine_extensions_built", False): |
There was a problem hiding this comment.
[Suggestion] _engine_extensions_built 标志会导致第一个 extension 构建后跳过所有后续 extension。如果将来有人在 ext_modules 中添加第二个非引擎的 Extension,它会被静默跳过。建议将判断条件限定到引擎模块名称:
if ext.name.startswith("openviking.storage.vectordb.engine") and getattr(self, "_engine_extensions_built", False):
return| def fake_find_spec(name, package=None): | ||
| fullname = importlib.util.resolve_name(name, package) if name.startswith(".") else name | ||
| if fullname == "openviking.storage.vectordb.engine._x86_caps": | ||
| return object() |
There was a problem hiding this comment.
[Suggestion] fake_find_spec 对匹配的模块返回裸 object()。目前生产代码只做 is not None 检查所以能通过,但如果 _module_exists 的实现后续扩展为访问 .origin 等 ModuleSpec 属性,测试会以难以理解的方式失败。建议返回 types.SimpleNamespace(origin="/fake/path.so") 以更真实地模拟。
| "bin/ov.exe", | ||
| "storage/vectordb/engine/*.so", | ||
| "storage/vectordb/engine/*.pyd", | ||
| ] |
There was a problem hiding this comment.
[Suggestion] glob 模式 storage/vectordb/engine/*.so 只匹配 engine/ 目录下一级的 .so 文件。如果将来 engine 下有嵌套子包,这些模式无法捕获。当前架构下不是问题,但 **/*.so 会更具前瞻性。
feat: split vectordb engine by cpu variant
背景
当前 vectordb 原生引擎的构建和分发方式更偏向单一扩展产物。这个模型在 x86 平台上有两个明显问题:
这会导致“兼容性”和“性能”之间只能二选一,也让 wheel 的跨机器复用能力受限。
目标
本 PR 的目标是把 vectordb engine 从“单一原生扩展”升级为“按 CPU 变体构建 + 运行时自动选择”的模式:
方案概述
整体方案分成两层:
1. 构建层
_x86_sse3_x86_avx2_x86_avx512_native后端_x86_caps模块,用于运行时探测 CPU 支持的指令集能力2. 运行时加载层
openviking.storage.vectordb.engine包装层作为稳定入口核心改动
1. 新增构建配置抽象
新增
build_support/x86_profiles.py,统一管理以下逻辑:关键设计:
sse3 / avx2 / avx512sse3作为 baseline,保证最小兼容面OV_X86_BUILD_VARIANTS环境变量裁剪构建变体2. 重构 setup.py 的原生扩展构建入口
setup.py不再假设只有一个固定的engine扩展,而是:build_support.x86_profiles推导宿主机构建配置openviking.storage.vectordb.engine._x86_sse3openviking.storage.vectordb.engine._native同时补充了打包规则,确保
storage/vectordb/engine/*.so和*.pyd能进入 wheel。3. 重构 CMake 为多后端构建模型
src/CMakeLists.txt从“单模块 + 单一 SIMD level”改为“公共库 + 多变体 backend”:engine_common作为公共实现_x86_capsengine_impl兼容 target,避免现有 C++ 测试/依赖直接断掉其中 x86 变体的编译规则为:
sse3使用-msse3avx2使用-mavx2,并显式关闭更高 AVX512 指令avx512使用-mavx512f/-bw/-dq/-vl如果编译器不支持某个变体,对应变体会被跳过,而不是直接让整个构建失败。
4. 让 pybind11 接口支持动态模块名
src/pybind11_interface.cpp通过OV_PY_MODULE_NAME宏接收模块名,使同一份绑定代码可以复用生成多个 Python 扩展:_x86_sse3_x86_avx2_x86_avx512_native这一步是多产物构建能够成立的基础。
5. 新增统一运行时 loader
新增
openviking/storage/vectordb/engine/__init__.py,负责:_x86_caps获取 CPU 支持能力AVX512 -> AVX2 -> SSE3的优先级自动选择最优后端OV_ENGINE_VARIANT强制指定 backend这样上层仍然只需要:
不需要关心底层真实加载的是哪个原生模块。
6. 新增 CPU 特性探测模块
新增
src/cpu_feature_probe.cpp,在 x86 上通过cpuid + xgetbv探测:这里不仅检查硬件 feature bit,也检查 OS 是否启用了对应寄存器上下文保存能力,避免“CPU 声称支持但运行时不可安全使用”的误判。
7. 调整 Python fallback 行为
openviking/storage/vectordb/store/bytes_row.py现在在导入 engine 后还会检查:ENGINE_VARIANT是否为unavailable如果 backend 不可用,则主动退回 Python 实现,而不是继续使用一个不完整的原生入口。
用户可见行为变化
本 PR 合入后:
_native为什么这样设计
这个方案的主要考虑有三点:
1. 把兼容性和性能解耦
通过同时打包
sse3baseline 和高阶变体,不再需要在“能跑”和“跑得快”之间二选一。2. 对上层调用侧保持稳定
业务层继续依赖统一的
openviking.storage.vectordb.engine,避免底层产物拆分扩散到上层接口。3. 让 wheel 分发更符合真实运行环境
相比在构建时硬编码单一 SIMD level,运行时基于实际 CPU 能力选择 backend,更符合 wheel 在多种机器上安装和运行的方式。
风险与兼容性评估
风险
已做的兼容性处理
sse3作为 x86 baseline_nativeengine_impl兼容 targetbytes_row.py增加 unavailable 检测并保留 Python fallback.so是否存在测试
新增测试覆盖:
tests/misc/test_vectordb_engine_loader.pytests/misc/test_x86_profiles.py本地已执行:
结果:
5 passed后续可关注项
OV_ENGINE_VARIANT不同取值的更多边界测试