Skip to content

Commit c3cd486

Browse files
feat: 添加 IndexHNSWFlatOptimized 支持和构建系统简化
- 添加 IndexHNSWFlatOptimized pybind11 绑定,支持 reorder_gorder() - 简化 deploy.sh,仅保留 PyCANDYAlgo 构建 - 简化 CI/CD workflow - 更新 faiss_HNSW_Optimized 算法适配新接口 - 修复 compute_gt.py 和 export_results.py 的 import 问题 - 添加 simple runbook 到重要实验列表
1 parent a253fef commit c3cd486

11 files changed

Lines changed: 589 additions & 769 deletions

File tree

.github/workflows/build-test.yml

Lines changed: 54 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
name: Build and Test
1+
name: Build and Test PyCANDYAlgo
22

33
on:
44
push:
@@ -8,18 +8,14 @@ on:
88
workflow_dispatch: # 允许手动触发
99

1010
jobs:
11-
test-deployment:
11+
build-pycandyalgo:
1212
runs-on: ubuntu-22.04
13-
# test 分支做快速测试,main 和 main-dev 做完整测试
14-
# 所有分支都跳过 VSAG(通过 CI 环境变量自动处理)
15-
env:
16-
FAST_BUILD: ${{ github.ref == 'refs/heads/test' && 'true' || 'false' }}
1713

1814
steps:
1915
- name: Checkout code
2016
uses: actions/checkout@v4
2117
with:
22-
submodules: recursive # 初始化所有 submodules
18+
submodules: recursive
2319

2420
- name: Set up Python 3.10
2521
uses: actions/setup-python@v5
@@ -30,7 +26,7 @@ jobs:
3026
uses: actions/cache@v4
3127
with:
3228
path: ~/.cache/pip
33-
key: ${{ runner.os }}-pip-${{ hashFiles('requirements.txt') }}
29+
key: ${{ runner.os }}-pip-torch-numpy
3430
restore-keys: |
3531
${{ runner.os }}-pip-
3632
@@ -39,10 +35,7 @@ jobs:
3935
with:
4036
path: |
4137
algorithms_impl/build
42-
algorithms_impl/faiss/build
43-
algorithms_impl/SPTAG/Release
44-
algorithms_impl/DiskANN/build
45-
key: ${{ runner.os }}-cmake-${{ hashFiles('algorithms_impl/CMakeLists.txt', 'algorithms_impl/**/*.cmake') }}
38+
key: ${{ runner.os }}-cmake-${{ hashFiles('algorithms_impl/CMakeLists.txt') }}
4639
restore-keys: |
4740
${{ runner.os }}-cmake-
4841
@@ -55,109 +48,82 @@ jobs:
5548
git \
5649
pkg-config \
5750
libgflags-dev \
51+
libgoogle-glog-dev \
52+
libfmt-dev \
5853
libboost-all-dev \
5954
libomp-dev \
60-
wget \
61-
curl
55+
libnuma-dev \
56+
libaio-dev
6257
63-
- name: Clean old build artifacts
58+
- name: Install Intel MKL
6459
run: |
65-
# 只清理 .so 文件,保留 build 缓存
66-
rm -rf algorithms_impl/PyCANDYAlgo*.so
67-
60+
wget -qO - https://apt.repos.intel.com/intel-gpg-keys/GPG-PUB-KEY-INTEL-SW-PRODUCTS.PUB | sudo apt-key add -
61+
echo "deb https://apt.repos.intel.com/oneapi all main" | sudo tee /etc/apt/sources.list.d/oneAPI.list
62+
sudo apt-get update
63+
sudo apt-get install -y intel-oneapi-mkl-devel || echo "MKL installation warning (non-fatal)"
64+
6865
- name: Run deployment script
6966
run: |
7067
chmod +x deploy.sh
71-
# 设置并行编译数量
72-
export MAKEFLAGS="-j$(nproc)"
73-
export CMAKE_BUILD_PARALLEL_LEVEL=$(nproc)
74-
75-
# CI 环境变量已设置,deploy.sh 会自动跳过 VSAG
76-
# test 分支:快速构建
77-
# main/main-dev 分支:完整构建
78-
if [ "${{ env.FAST_BUILD }}" = "true" ]; then
79-
echo "🚀 快速构建模式(test 分支,跳过 VSAG)"
80-
else
81-
echo "🔨 完整构建模式(main/main-dev 分支,跳过 VSAG)"
82-
fi
83-
./deploy.sh
84-
timeout-minutes: 60
85-
env:
86-
# 加速 pip 安装
87-
PIP_NO_CACHE_DIR: false
88-
PIP_DISABLE_PIP_VERSION_CHECK: 1
68+
./deploy.sh --skip-system-deps
69+
timeout-minutes: 45
8970

90-
- name: Verify Python packages
71+
- name: Verify PyCANDYAlgo import
9172
run: |
9273
source sage-db-bench/bin/activate
93-
# 确认使用正确的 Python
74+
75+
echo "=== Python Environment ==="
9476
which python3
9577
python3 --version
9678
97-
# 设置库路径
98-
TORCH_LIB=$(python3 -c "import torch; import os; print(os.path.join(os.path.dirname(torch.__file__), 'lib'))")
99-
export LD_LIBRARY_PATH="$TORCH_LIB:$LD_LIBRARY_PATH"
100-
101-
# 检查 .so 文件和符号表
102-
echo "检查 PyCANDYAlgo.so 文件..."
79+
echo ""
80+
echo "=== Check .so file ==="
10381
SO_FILE=$(find algorithms_impl -name "PyCANDYAlgo*.so" -type f | head -1)
10482
if [ -n "$SO_FILE" ]; then
105-
echo "找到: $SO_FILE"
106-
echo "文件大小: $(ls -lh "$SO_FILE" | awk '{print $5}')"
107-
echo ""
108-
echo "检查 PyInit 符号..."
109-
nm -D "$SO_FILE" | grep PyInit || echo "⚠ 未找到 PyInit 符号"
110-
echo ""
111-
echo "检查所有导出符号..."
112-
nm -D "$SO_FILE" | grep " T " | head -20
83+
echo "Found: $SO_FILE"
84+
echo "Size: $(ls -lh "$SO_FILE" | awk '{print $5}')"
11385
else
114-
echo "❌ 未找到 .so 文件"
86+
echo "❌ PyCANDYAlgo.so not found"
11587
exit 1
11688
fi
11789
118-
# 测试导入 PyCANDYAlgo(所有分支)
119-
cd algorithms_impl && python3 -c "import PyCANDYAlgo; print('✓ PyCANDYAlgo imported'); print(f' Version: {PyCANDYAlgo.__version__}'); print(f' Compiled: {PyCANDYAlgo.__compiled_time__}')" && cd ..
120-
121-
# 跳过 pyvsag 测试(所有分支都不构建 VSAG)
122-
# python3 -c "import pyvsag; print('✓ pyvsag imported')"
123-
124-
python3 -c "import numpy, torch, yaml; print('✓ Core dependencies OK')"
125-
126-
- name: Run unit tests
127-
run: |
128-
source sage-db-bench/bin/activate
129-
130-
# test 分支:快速测试(排除性能测试)
131-
if [ "${{ env.FAST_BUILD }}" = "true" ]; then
132-
echo "🧪 快速测试模式"
133-
pytest tests/ -v --tb=short --ignore=tests/test_performance.py -m "not slow" || true
134-
else
135-
echo "🧪 完整测试模式"
136-
pytest tests/ -v --tb=short || true
137-
fi
138-
139-
- name: Check algorithm availability
90+
echo ""
91+
echo "=== Test PyCANDYAlgo Import ==="
92+
python3 -c "
93+
import PyCANDYAlgo
94+
print('✅ PyCANDYAlgo imported successfully')
95+
print(f' Version: {PyCANDYAlgo.__version__}')
96+
print(f' Compiled: {PyCANDYAlgo.__compiled_time__}')
97+
98+
# Test IndexHNSWFlatOptimized
99+
print()
100+
print('Available classes:')
101+
print(f' - IndexHNSWFlatOptimized: {hasattr(PyCANDYAlgo, \"IndexHNSWFlatOptimized\")}')
102+
print(f' - MetricType: {hasattr(PyCANDYAlgo, \"MetricType\")}')
103+
"
104+
105+
- name: Test core dependencies
140106
run: |
141107
source sage-db-bench/bin/activate
142-
python -c "
143-
from bench.algorithms import get_available_algorithms
144-
algos = get_available_algorithms()
145-
print(f'Available algorithms: {len(algos)}')
146-
for algo in algos:
147-
print(f' - {algo}')
148-
" || echo "Algorithm check skipped"
149-
108+
python3 -c "
109+
import numpy
110+
import torch
111+
print('✅ numpy:', numpy.__version__)
112+
print('✅ torch:', torch.__version__)
113+
"
114+
150115
- name: Upload build logs on failure
151116
if: failure()
152117
uses: actions/upload-artifact@v4
153118
with:
154119
name: build-logs
155120
path: |
156-
algorithms_impl/build.log
157-
algorithms_impl/build/CMakeFiles/*.log
121+
algorithms_impl/build/cmake_config.log
122+
algorithms_impl/build/CMakeFiles/CMakeError.log
123+
algorithms_impl/build/CMakeFiles/CMakeOutput.log
158124
retention-days: 7
159125
160-
lint-and-format:
126+
lint:
161127
runs-on: ubuntu-22.04
162128
163129
steps:
@@ -170,17 +136,8 @@ jobs:
170136
python-version: '3.10'
171137
172138
- name: Install linting tools
173-
run: |
174-
pip install flake8 black isort mypy
139+
run: pip install flake8
175140
176-
- name: Run flake8
141+
- name: Run flake8 (syntax errors only)
177142
run: |
178143
flake8 bench/ datasets/ --count --select=E9,F63,F7,F82 --show-source --statistics || true
179-
180-
- name: Check code formatting
181-
run: |
182-
black --check bench/ datasets/ || true
183-
184-
- name: Check import sorting
185-
run: |
186-
isort --check-only bench/ datasets/ || true

algorithms_impl/bindings/PyCANDY.cpp

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include <DiskANN/python/include/builder.h>
2626
//#endif
2727
#include <faiss/index_factory.h>
28+
#include <faiss/IndexHNSWOptimized.h>
2829

2930

3031
#include<puck/pyapi_wrapper/py_api_wrapper.h>
@@ -366,7 +367,43 @@ PYBIND11_MODULE(PyCANDYAlgo, m) {
366367

367368
m.def("index_factory_ip", &faiss::index_factory_IP, "Create custom index from faiss with IP");
368369

369-
m.def("index_factory_l2", &faiss::index_factory_L2, "Create custom index from faiss with IP");
370+
m.def("index_factory_l2", &faiss::index_factory_L2, "Create custom index from faiss with L2");
371+
372+
/// Metric type enum for faiss (needed before IndexHNSW classes)
373+
py::enum_<faiss::MetricType>(m, "MetricType")
374+
.value("METRIC_L2", faiss::METRIC_L2)
375+
.value("METRIC_INNER_PRODUCT", faiss::METRIC_INNER_PRODUCT)
376+
.export_values();
377+
378+
/// IndexHNSWFlatOptimized - HNSW index with Flat storage and Gorder optimization
379+
/// This is the main class to use for HNSW with Gorder reordering
380+
py::class_<faiss::IndexHNSWFlatOptimized, std::shared_ptr<faiss::IndexHNSWFlatOptimized>>(m, "IndexHNSWFlatOptimized")
381+
.def(py::init<int, int, faiss::MetricType>(),
382+
py::arg("d"), py::arg("M") = 32, py::arg("metric") = faiss::METRIC_L2,
383+
"Create HNSW index with Flat storage.\n"
384+
"Args:\n"
385+
" d: vector dimension\n"
386+
" M: number of neighbors per node (default 32)\n"
387+
" metric: distance metric (METRIC_L2 or METRIC_INNER_PRODUCT)")
388+
.def("add", &faiss::IndexHNSWFlatOptimized::add_arrays,
389+
"Add vectors to the index")
390+
.def("search", &faiss::IndexHNSWFlatOptimized::search_arrays,
391+
py::arg("n"), py::arg("x"), py::arg("k"), py::arg("ef_search"),
392+
"Search k nearest neighbors with given efSearch parameter")
393+
.def("train", &faiss::IndexHNSWFlatOptimized::train_arrays,
394+
"Train the index (no-op for Flat storage)")
395+
.def("reset", &faiss::IndexHNSWFlatOptimized::reset,
396+
"Remove all vectors from the index")
397+
.def("reorder_gorder", &faiss::IndexHNSWFlatOptimized::reorder_gorder,
398+
py::arg("window") = 5,
399+
"Reorder the HNSW graph using Gorder algorithm for better cache locality.\n"
400+
"Args:\n"
401+
" window: sliding window size for Gorder algorithm (default 5)")
402+
.def_readwrite("verbose", &faiss::IndexHNSWFlatOptimized::verbose)
403+
.def_readonly("ntotal", &faiss::IndexHNSWFlatOptimized::ntotal,
404+
"Total number of vectors in the index")
405+
.def_readonly("d", &faiss::IndexHNSWFlatOptimized::d,
406+
"Vector dimension");
370407

371408

372409

0 commit comments

Comments
 (0)