Skip to content

Commit 55af248

Browse files
committed
feat: add portable x86 simd dispatch build profile
refactor: default source x86 builds to portable profile
1 parent cae2c37 commit 55af248

21 files changed

Lines changed: 829 additions & 8 deletions

CONTRIBUTING.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,19 @@ uv pip install -e . --force-reinstall
5959

6060
This command ensures that `setup.py` is re-executed, triggering the compilation of AGFS and C++ components.
6161

62+
For x86 builds, OpenViking now uses explicit build profiles:
63+
64+
- `wheel` builds default to `OV_X86_BUILD_PROFILE=portable`, which emits a single portable extension with runtime SIMD dispatch (`SSE3` baseline plus optional `AVX2/AVX512` kernels).
65+
- source installs also default to `OV_X86_BUILD_PROFILE=portable`, so local editable installs match wheel behavior and stay portable across x86 machines.
66+
- `OV_X86_BUILD_PROFILE=native` remains available when you explicitly want `-march=native` for same-machine performance.
67+
- advanced users can override with `OV_X86_BUILD_PROFILE=fixed` and `OV_X86_SIMD_LEVEL=SSE3|AVX2|AVX512`.
68+
69+
Example native source build:
70+
71+
```bash
72+
OV_X86_BUILD_PROFILE=native uv pip install -e . --force-reinstall
73+
```
74+
6275
### 3. Configure Environment
6376

6477
Create a configuration file `~/.openviking/ov.conf`:

CONTRIBUTING_CN.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,19 @@ uv pip install -e . --force-reinstall
5959

6060
该命令会强制重新执行 `setup.py`,触发 AGFS 和 C++ 组件的编译与安装。
6161

62+
对于 x86 构建,OpenViking 现在提供明确的构建 profile:
63+
64+
- `wheel` 默认使用 `OV_X86_BUILD_PROFILE=portable`,产出单个可移植扩展,并在运行时自动在 `SSE3` 基线与可选的 `AVX2/AVX512` kernel 之间分派。
65+
- 源码安装也默认使用 `OV_X86_BUILD_PROFILE=portable`,让本地 editable 安装与 wheel 行为一致,并保持跨 x86 机器可移植。
66+
- 如果你明确需要本机最佳性能,仍可显式设置 `OV_X86_BUILD_PROFILE=native` 以启用 `-march=native`
67+
- 高级用户可以用 `OV_X86_BUILD_PROFILE=fixed` 配合 `OV_X86_SIMD_LEVEL=SSE3|AVX2|AVX512` 强制固定 ISA。
68+
69+
本机优化源码构建示例:
70+
71+
```bash
72+
OV_X86_BUILD_PROFILE=native uv pip install -e . --force-reinstall
73+
```
74+
6275
### 3. 配置环境
6376

6477
创建配置文件 `~/.openviking/ov.conf`

MANIFEST.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ include LICENSE
1010
include README.md
1111
include pyproject.toml
1212
include setup.py
13+
recursive-include build_support *.py
1314
recursive-include openviking *.yaml
1415

1516
# sdist should be source-only: never ship runtime binaries from working tree

build_support/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
"""Build-time helpers for OpenViking packaging."""

build_support/x86_profiles.py

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
from __future__ import annotations
2+
3+
from dataclasses import dataclass
4+
from typing import Mapping, Sequence
5+
6+
_SUPPORTED_PROFILES = {"portable", "native", "fixed"}
7+
_SUPPORTED_LEVELS = {"SSE3", "AVX2", "AVX512", "NATIVE"}
8+
9+
10+
@dataclass(frozen=True)
11+
class X86BuildConfig:
12+
profile: str
13+
simd_level: str
14+
baseline: str
15+
dispatch_enabled: bool
16+
17+
18+
def _normalize_profile(profile: str) -> str:
19+
normalized = profile.strip().lower()
20+
if normalized not in _SUPPORTED_PROFILES:
21+
raise ValueError(
22+
f"Unsupported OV_X86_BUILD_PROFILE={profile!r}; "
23+
"expected one of: fixed, native, portable"
24+
)
25+
return normalized
26+
27+
28+
def _normalize_level(level: str) -> str:
29+
normalized = level.strip().upper()
30+
if normalized not in _SUPPORTED_LEVELS:
31+
raise ValueError(
32+
f"Unsupported OV_X86_SIMD_LEVEL={level!r}; expected one of: SSE3, AVX2, AVX512, NATIVE"
33+
)
34+
return normalized
35+
36+
37+
def _is_wheel_build(argv: Sequence[str]) -> bool:
38+
wheel_commands = {"bdist_wheel", "editable_wheel"}
39+
return any(arg in wheel_commands for arg in argv)
40+
41+
42+
def resolve_x86_build_config(
43+
env: Mapping[str, str] | None = None,
44+
argv: Sequence[str] | None = None,
45+
) -> X86BuildConfig:
46+
env = env or {}
47+
argv = argv or []
48+
49+
baseline = _normalize_level(env.get("OV_X86_PORTABLE_BASELINE", "SSE3"))
50+
if baseline == "NATIVE":
51+
raise ValueError("OV_X86_PORTABLE_BASELINE cannot be NATIVE")
52+
53+
explicit_profile = env.get("OV_X86_BUILD_PROFILE")
54+
explicit_level = env.get("OV_X86_SIMD_LEVEL")
55+
56+
if explicit_profile:
57+
profile = _normalize_profile(explicit_profile)
58+
elif explicit_level:
59+
normalized_level = _normalize_level(explicit_level)
60+
profile = "native" if normalized_level == "NATIVE" else "fixed"
61+
else:
62+
profile = "portable"
63+
64+
if profile == "portable":
65+
return X86BuildConfig(
66+
profile="portable",
67+
simd_level=baseline,
68+
baseline=baseline,
69+
dispatch_enabled=True,
70+
)
71+
72+
if profile == "native":
73+
return X86BuildConfig(
74+
profile="native",
75+
simd_level="NATIVE",
76+
baseline=baseline,
77+
dispatch_enabled=False,
78+
)
79+
80+
simd_level = _normalize_level(explicit_level or "AVX2")
81+
if simd_level == "NATIVE":
82+
raise ValueError("OV_X86_SIMD_LEVEL=NATIVE requires OV_X86_BUILD_PROFILE=native")
83+
84+
return X86BuildConfig(
85+
profile="fixed",
86+
simd_level=simd_level,
87+
baseline=baseline,
88+
dispatch_enabled=False,
89+
)

setup.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
from setuptools import Extension, setup
1111
from setuptools.command.build_ext import build_ext
1212

13+
from build_support.x86_profiles import resolve_x86_build_config
14+
1315
CMAKE_PATH = shutil.which("cmake") or "cmake"
1416
C_COMPILER_PATH = shutil.which("gcc") or "gcc"
1517
CXX_COMPILER_PATH = shutil.which("g++") or "g++"
@@ -335,6 +337,13 @@ def _build_extension_impl(self, ext_fullpath, ext_dir, build_dir):
335337
"""Invoke CMake to build the Python native extension."""
336338
py_output_name = ext_fullpath.stem
337339
py_output_suffix = ext_fullpath.suffix
340+
x86_build_config = resolve_x86_build_config(os.environ, sys.argv)
341+
print(
342+
"Configuring x86 build profile="
343+
f"{x86_build_config.profile} simd={x86_build_config.simd_level} "
344+
f"baseline={x86_build_config.baseline} "
345+
f"dispatch={'ON' if x86_build_config.dispatch_enabled else 'OFF'}"
346+
)
338347

339348
cmake_args = [
340349
f"-S{Path(ENGINE_SOURCE_DIR).resolve()}",
@@ -351,7 +360,10 @@ def _build_extension_impl(self, ext_fullpath, ext_dir, build_dir):
351360
f"-Dpybind11_DIR={pybind11.get_cmake_dir()}",
352361
f"-DCMAKE_C_COMPILER={C_COMPILER_PATH}",
353362
f"-DCMAKE_CXX_COMPILER={CXX_COMPILER_PATH}",
354-
f"-DOV_X86_SIMD_LEVEL={os.environ.get('OV_X86_SIMD_LEVEL', 'AVX2')}",
363+
f"-DOV_X86_BUILD_PROFILE={x86_build_config.profile}",
364+
f"-DOV_X86_SIMD_LEVEL={x86_build_config.simd_level}",
365+
f"-DOV_X86_PORTABLE_BASELINE={x86_build_config.baseline}",
366+
f"-DOV_X86_DISPATCH={'ON' if x86_build_config.dispatch_enabled else 'OFF'}",
355367
]
356368

357369
if sys.platform == "darwin":

src/CMakeLists.txt

Lines changed: 90 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,29 +4,64 @@ project(openviking_cpp)
44

55
include(CheckCXXCompilerFlag)
66

7-
set(OV_X86_SIMD_LEVEL "AVX2" CACHE STRING "x86 SIMD level: SSE3|AVX2|AVX512|NATIVE")
7+
set(OV_X86_BUILD_PROFILE "portable" CACHE STRING "x86 build profile: portable|native|fixed")
8+
set_property(CACHE OV_X86_BUILD_PROFILE PROPERTY STRINGS portable native fixed)
9+
set(OV_X86_SIMD_LEVEL "AVX2" CACHE STRING "x86 SIMD level for fixed/native builds: SSE3|AVX2|AVX512|NATIVE")
810
set_property(CACHE OV_X86_SIMD_LEVEL PROPERTY STRINGS SSE3 AVX2 AVX512 NATIVE)
11+
set(OV_X86_PORTABLE_BASELINE "SSE3" CACHE STRING "x86 portable baseline ISA")
12+
set_property(CACHE OV_X86_PORTABLE_BASELINE PROPERTY STRINGS SSE3)
13+
option(OV_X86_DISPATCH "Enable runtime SIMD dispatch on x86" OFF)
914

1015
set(OV_PLATFORM_X86 OFF)
1116
if(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|amd64|AMD64|i[3-6]86")
1217
set(OV_PLATFORM_X86 ON)
1318
endif()
1419

20+
set(OV_X86_AVX2_DISPATCH_SOURCES
21+
${CMAKE_CURRENT_SOURCE_DIR}/index/detail/vector/common/x86/l2_avx2.cpp
22+
${CMAKE_CURRENT_SOURCE_DIR}/index/detail/vector/common/x86/ip_avx2.cpp
23+
${CMAKE_CURRENT_SOURCE_DIR}/index/detail/vector/common/x86/int8_avx2.cpp
24+
)
25+
set(OV_X86_AVX512_DISPATCH_SOURCES
26+
${CMAKE_CURRENT_SOURCE_DIR}/index/detail/vector/common/x86/l2_avx512.cpp
27+
${CMAKE_CURRENT_SOURCE_DIR}/index/detail/vector/common/x86/ip_avx512.cpp
28+
)
29+
30+
set(OV_X86_PORTABLE_OPTIONAL_SOURCES
31+
${OV_X86_AVX2_DISPATCH_SOURCES}
32+
${OV_X86_AVX512_DISPATCH_SOURCES}
33+
)
34+
1535
if(OV_PLATFORM_X86)
36+
string(TOUPPER "${OV_X86_BUILD_PROFILE}" OV_X86_BUILD_PROFILE_UPPER)
1637
string(TOUPPER "${OV_X86_SIMD_LEVEL}" OV_X86_SIMD_LEVEL_UPPER)
38+
string(TOUPPER "${OV_X86_PORTABLE_BASELINE}" OV_X86_PORTABLE_BASELINE_UPPER)
1739
set(OV_X86_COMPILE_FLAGS)
1840

19-
if(OV_X86_SIMD_LEVEL_UPPER STREQUAL "NATIVE")
41+
if(OV_X86_BUILD_PROFILE_UPPER STREQUAL "PORTABLE")
42+
set(OV_X86_DISPATCH ON)
43+
if(NOT OV_X86_PORTABLE_BASELINE_UPPER STREQUAL "SSE3")
44+
message(FATAL_ERROR "OV_X86_PORTABLE_BASELINE currently only supports SSE3")
45+
endif()
46+
47+
check_cxx_compiler_flag("-msse3" HAVE_SSE3)
48+
if(HAVE_SSE3)
49+
list(APPEND OV_X86_COMPILE_FLAGS -msse3)
50+
else()
51+
message(FATAL_ERROR "Compiler does not support -msse3 for portable x86 builds")
52+
endif()
53+
elseif(OV_X86_BUILD_PROFILE_UPPER STREQUAL "NATIVE" OR OV_X86_SIMD_LEVEL_UPPER STREQUAL "NATIVE")
2054
check_cxx_compiler_flag("-march=native" HAVE_MARCH_NATIVE)
2155
if(HAVE_MARCH_NATIVE)
2256
list(APPEND OV_X86_COMPILE_FLAGS -march=native)
2357
else()
24-
message(WARNING "-march=native is not supported by this compiler; falling back to AVX2")
58+
message(WARNING "-march=native is not supported by this compiler; falling back to fixed AVX2")
59+
set(OV_X86_BUILD_PROFILE_UPPER "FIXED")
2560
set(OV_X86_SIMD_LEVEL_UPPER "AVX2")
2661
endif()
2762
endif()
2863

29-
if(OV_X86_SIMD_LEVEL_UPPER STREQUAL "AVX512")
64+
if(OV_X86_BUILD_PROFILE_UPPER STREQUAL "FIXED" AND OV_X86_SIMD_LEVEL_UPPER STREQUAL "AVX512")
3065
foreach(FLAG -mavx512f -mavx512bw -mavx512dq -mavx512vl)
3166
string(REPLACE "-" "_" FLAG_VAR_SUFFIX "${FLAG}")
3267
set(FLAG_VAR "HAVE_${FLAG_VAR_SUFFIX}")
@@ -36,7 +71,7 @@ if(OV_PLATFORM_X86)
3671
endif()
3772
list(APPEND OV_X86_COMPILE_FLAGS ${FLAG})
3873
endforeach()
39-
elseif(OV_X86_SIMD_LEVEL_UPPER STREQUAL "AVX2")
74+
elseif(OV_X86_BUILD_PROFILE_UPPER STREQUAL "FIXED" AND OV_X86_SIMD_LEVEL_UPPER STREQUAL "AVX2")
4075
check_cxx_compiler_flag("-mavx2" HAVE_MAVX2)
4176
if(HAVE_MAVX2)
4277
list(APPEND OV_X86_COMPILE_FLAGS -mavx2)
@@ -55,7 +90,7 @@ if(OV_PLATFORM_X86)
5590
endif()
5691
endif()
5792

58-
if(OV_X86_SIMD_LEVEL_UPPER STREQUAL "SSE3")
93+
if(OV_X86_BUILD_PROFILE_UPPER STREQUAL "FIXED" AND OV_X86_SIMD_LEVEL_UPPER STREQUAL "SSE3")
5994
check_cxx_compiler_flag("-msse3" HAVE_SSE3)
6095
if(HAVE_SSE3)
6196
list(APPEND OV_X86_COMPILE_FLAGS -msse3)
@@ -69,7 +104,41 @@ if(OV_PLATFORM_X86)
69104
add_compile_options(${OV_X86_COMPILE_FLAGS})
70105
endif()
71106

72-
message(STATUS "OpenViking x86 SIMD level: ${OV_X86_SIMD_LEVEL_UPPER}")
107+
if(OV_X86_DISPATCH)
108+
add_compile_definitions(OV_X86_RUNTIME_DISPATCH=1)
109+
check_cxx_compiler_flag("-mavx2" HAVE_PORTABLE_MAVX2)
110+
if(HAVE_PORTABLE_MAVX2)
111+
set_source_files_properties(${OV_X86_AVX2_DISPATCH_SOURCES}
112+
PROPERTIES COMPILE_OPTIONS "-mavx2")
113+
add_compile_definitions(OV_X86_AVX2_DISPATCH_COMPILED=1)
114+
endif()
115+
116+
set(OV_X86_AVX512_COMPILE_FLAGS)
117+
set(OV_CAN_BUILD_AVX512_DISPATCH ON)
118+
foreach(FLAG -mavx512f -mavx512bw -mavx512dq -mavx512vl)
119+
string(REPLACE "-" "_" FLAG_VAR_SUFFIX "${FLAG}")
120+
set(FLAG_VAR "HAVE_PORTABLE_${FLAG_VAR_SUFFIX}")
121+
check_cxx_compiler_flag("${FLAG}" ${FLAG_VAR})
122+
if(${FLAG_VAR})
123+
list(APPEND OV_X86_AVX512_COMPILE_FLAGS ${FLAG})
124+
else()
125+
set(OV_CAN_BUILD_AVX512_DISPATCH OFF)
126+
endif()
127+
endforeach()
128+
129+
if(OV_CAN_BUILD_AVX512_DISPATCH)
130+
set_source_files_properties(${OV_X86_AVX512_DISPATCH_SOURCES}
131+
PROPERTIES COMPILE_OPTIONS "${OV_X86_AVX512_COMPILE_FLAGS}")
132+
add_compile_definitions(OV_X86_AVX512_DISPATCH_COMPILED=1)
133+
endif()
134+
endif()
135+
136+
message(STATUS "OpenViking x86 build profile: ${OV_X86_BUILD_PROFILE_UPPER}")
137+
if(OV_X86_DISPATCH)
138+
message(STATUS "OpenViking x86 portable baseline: ${OV_X86_PORTABLE_BASELINE_UPPER}")
139+
else()
140+
message(STATUS "OpenViking x86 SIMD level: ${OV_X86_SIMD_LEVEL_UPPER}")
141+
endif()
73142
endif()
74143

75144
set(CMAKE_CXX_STANDARD 17)
@@ -153,6 +222,20 @@ file(GLOB_RECURSE ALL_SOURCES
153222
"common/*.cpp"
154223
)
155224

225+
list(REMOVE_ITEM ALL_SOURCES ${OV_X86_PORTABLE_OPTIONAL_SOURCES})
226+
227+
set(OV_X86_PORTABLE_SOURCES)
228+
if(OV_PLATFORM_X86 AND OV_X86_DISPATCH)
229+
if(HAVE_PORTABLE_MAVX2)
230+
list(APPEND OV_X86_PORTABLE_SOURCES ${OV_X86_AVX2_DISPATCH_SOURCES})
231+
endif()
232+
if(OV_CAN_BUILD_AVX512_DISPATCH)
233+
list(APPEND OV_X86_PORTABLE_SOURCES ${OV_X86_AVX512_DISPATCH_SOURCES})
234+
endif()
235+
endif()
236+
237+
list(APPEND ALL_SOURCES ${OV_X86_PORTABLE_SOURCES})
238+
156239
add_library(
157240
engine_impl
158241
STATIC

0 commit comments

Comments
 (0)