Skip to content

Commit 527248f

Browse files
Merge pull request #2 from sunhailin-Leo/dev
feat: v1.1.0
2 parents 212cba1 + 9561983 commit 527248f

18 files changed

Lines changed: 3904 additions & 2140 deletions

.github/workflows/ci.yml

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
- master
8+
pull_request:
9+
10+
jobs:
11+
test:
12+
name: Test (Go ${{ matrix.go }}, ${{ matrix.os }})
13+
runs-on: ${{ matrix.os }}
14+
timeout-minutes: 15
15+
strategy:
16+
fail-fast: false
17+
matrix:
18+
os: [ubuntu-latest, macos-latest, windows-latest]
19+
go: ['1.21', '1.22', '1.23', '1.24', '1.25', '1.26']
20+
21+
steps:
22+
- name: Checkout code
23+
uses: actions/checkout@v4
24+
with:
25+
submodules: recursive
26+
27+
- name: Set up Go
28+
uses: actions/setup-go@v5
29+
with:
30+
go-version: ${{ matrix.go }}
31+
cache: true
32+
33+
- name: Install C++ compiler (Ubuntu)
34+
if: runner.os == 'Linux'
35+
run: |
36+
sudo apt-get update
37+
sudo apt-get install -y g++
38+
39+
- name: Install C++ compiler (macOS)
40+
if: runner.os == 'macOS'
41+
run: |
42+
clang++ --version
43+
44+
- name: Build (portable, Unix)
45+
if: runner.os != 'Windows'
46+
run: make portable
47+
48+
- name: Build (portable, Windows)
49+
if: runner.os == 'Windows'
50+
shell: bash
51+
run: |
52+
g++ -std=c++11 -I. -O2 -c hnsw_wrapper.cc
53+
ar rcs libhnsw.a hnsw_wrapper.o
54+
CGO_CXXFLAGS="-I. -std=c++11" go build
55+
56+
- name: Run tests (Unix)
57+
if: runner.os != 'Windows'
58+
run: make test
59+
60+
- name: Run tests (Windows)
61+
if: runner.os == 'Windows'
62+
shell: bash
63+
run: |
64+
CGO_CXXFLAGS="-I. -std=c++11" go test -v -count=1 -timeout 120s ./...
65+
66+
benchmark:
67+
name: Benchmark
68+
runs-on: ubuntu-latest
69+
timeout-minutes: 15
70+
strategy:
71+
fail-fast: false
72+
matrix:
73+
go: ['1.23']
74+
75+
steps:
76+
- name: Checkout code
77+
uses: actions/checkout@v4
78+
with:
79+
submodules: recursive
80+
81+
- name: Set up Go
82+
uses: actions/setup-go@v5
83+
with:
84+
go-version: ${{ matrix.go }}
85+
cache: true
86+
87+
- name: Install C++ compiler
88+
run: |
89+
sudo apt-get update
90+
sudo apt-get install -y g++
91+
92+
- name: Build (portable)
93+
run: make portable
94+
95+
- name: Run benchmarks
96+
run: make bench

CHANGELOG.md

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
# Changelog
2+
3+
All notable changes to this project will be documented in this file.
4+
5+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7+
8+
## [v1.1.0] - 2026-03-31
9+
10+
### Added
11+
- Synced hnswlib C++ headers to latest [nmslib/hnswlib master](https://github.com/nmslib/hnswlib/tree/master/hnswlib)
12+
- New `hnswlib/stop_condition.h` header from upstream
13+
- `NewWithReplaceDeleted()` — create index with replace-deleted support
14+
- `AddPointWithReplace()` — add vector reusing deleted slots
15+
- `Free()` — explicit memory release for HNSW index
16+
- Nil-safety checks for all public methods (return zero values instead of panicking)
17+
- Comprehensive unit tests: 19 test functions covering all space types (L2, IP, Cosine)
18+
- Performance benchmarks: 7 benchmark functions (AddPoint, SearchKNN, BatchOps, SaveLoad)
19+
- Multi-platform Makefile with `portable`, `opt`, `bench`, `lint` targets
20+
- Cross-compilation targets: `build-linux-amd64`, `build-linux-arm64`, `build-darwin-amd64`, `build-darwin-arm64`, `build-windows-amd64`
21+
- Windows support via MinGW-w64 (conditional CGO LDFLAGS, `uint64_t` types)
22+
- GitHub Actions CI: Linux, macOS, Windows matrix with Go 1.21–1.26
23+
24+
### Changed
25+
- Upgraded hnswlib from v0.7.0 to latest master
26+
- All C bridge types changed from `unsigned long int` to `uint64_t` for cross-platform safety (Windows LLP64 compatibility)
27+
- `SearchKNN` now uses `sync.Pool` to reuse C-type buffers — **allocs reduced 50%** (4 → 2), **L2 search ~12% faster**
28+
- `SearchBatchKNN` uses lock-free per-index writes instead of mutex — **allocs reduced 49%** (412 → 211)
29+
- `normalizeVector` optimized to in-place modification (no slice return/copy)
30+
- Removed unnecessary `runtime.GC()` call from `Free()`
31+
- `go.mod` bumped to Go 1.21
32+
33+
### Fixed
34+
- Fixed 3 upstream bugs in `hnswalg.h`: `internal_id``internalId` variable name
35+
- Fixed `UpdateBatchPoints` condition bug (`&&``||` for parameter validation)
36+
37+
## [v1.0.4]
38+
39+
### Added
40+
- `UpdatePoint()` — update vector for existing label
41+
- `UpdateBatchPoints()` — batch update with concurrent goroutines
42+
43+
## [v1.0.3]
44+
45+
### Added
46+
- `GetMaxElements()` — query maximum index capacity
47+
- `GetCurrentElementCount()` — query current element count
48+
- `GetDeleteCount()` — query soft-deleted element count
49+
- `GetVectorByLabel()` — retrieve stored vector by label
50+
51+
## [v1.0.2]
52+
53+
### Changed
54+
- Updated hnswlib to v0.7.0
55+
56+
### Added
57+
- Batch operations: `AddBatchPoints`, `SearchBatchKNN`
58+
- Soft delete: `MarkDelete`, `UnmarkDelete`, `GetLabelIsMarkedDeleted`
59+
- `ResizeIndex()` — dynamically resize index capacity
60+
61+
## [v1.0.1]
62+
63+
### Changed
64+
- Code formatting improvements
65+
- Experimental `Unload` API (deprecated in v1.1.0, use `Free`)
66+
67+
## [v1.0.0]
68+
69+
### Added
70+
- Initial release with hnswlib v0.5.2
71+
- Core API: `New`, `Load`, `Save`, `AddPoint`, `SearchKNN`, `SetEf`
72+
- Distance metrics: L2, Inner Product, Cosine

Makefile

Lines changed: 111 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,129 @@
1-
#
2-
# Copyright (c) 2016-present, Facebook, Inc.
3-
# All rights reserved.
4-
#
5-
# This source code is licensed under the MIT license found in the
6-
# LICENSE file in the root directory of this source tree.
7-
#
8-
CXX = c++
1+
# hnswlib-to-go Makefile
2+
# Builds the C++ static library and Go bindings for HNSW vector search.
3+
4+
CXX ?= c++
5+
AR ?= ar
96
INCLUDES = -I.
10-
CXXFLAGS = -pthread -std=c++0x -march=native -std=c++11 $(INCLUDES)
7+
CXXFLAGS = -pthread -std=c++11 $(INCLUDES)
118
OBJS = hnsw_wrapper.o
129

13-
opt: CXXFLAGS += -O3 -funroll-loops
10+
# Platform detection
11+
ifeq ($(OS),Windows_NT)
12+
UNAME_S := Windows
13+
UNAME_M := $(PROCESSOR_ARCHITECTURE)
14+
# Windows: MinGW environment
15+
CXX ?= g++
16+
CXXFLAGS = -std=c++11 $(INCLUDES)
17+
LDFLAGS_EXTRA = -lstdc++
18+
RM = del /Q
19+
LIB_EXT = .a
20+
else
21+
UNAME_S := $(shell uname -s)
22+
UNAME_M := $(shell uname -m)
23+
RM = rm -rf
24+
LIB_EXT = .a
25+
ifeq ($(UNAME_S),Darwin)
26+
CXXFLAGS += -stdlib=libc++
27+
LDFLAGS_EXTRA = -lc++
28+
else ifeq ($(UNAME_S),Linux)
29+
LDFLAGS_EXTRA = -lstdc++
30+
endif
31+
endif
32+
33+
# Default: optimized build with native arch
34+
.PHONY: all opt build clean test bench lint help
35+
36+
all: build
37+
38+
opt: CXXFLAGS += -O3 -funroll-loops -march=native
1439
opt: build
1540

41+
# Portable build without -march=native (for CI / cross-compile)
42+
portable: CXXFLAGS += -O2
43+
portable: build
44+
1645
coverage: CXXFLAGS += -O0 -fno-inline -fprofile-arcs --coverage
46+
coverage: build
47+
48+
# ---------- C++ compilation ----------
1749

1850
hnsw_wrapper.o: hnsw_wrapper.h hnsw_wrapper.cc hnswlib/*.h
1951
$(CXX) $(CXXFLAGS) -c hnsw_wrapper.cc
2052

2153
libhnsw.a: $(OBJS)
2254
$(AR) rcs libhnsw.a $(OBJS)
2355

24-
clean:
25-
rm -rf *.o libhnsw.a *.o *.gcno *.gcda hnsw
56+
# ---------- Go targets ----------
2657

2758
build: libhnsw.a
2859
env CGO_CXXFLAGS="$(INCLUDES) -std=c++11" go build
2960

3061
test: build
31-
go test
62+
env CGO_CXXFLAGS="$(INCLUDES) -std=c++11" go test -v -count=1 -timeout 120s ./...
63+
64+
bench: build
65+
env CGO_CXXFLAGS="$(INCLUDES) -std=c++11" go test -bench=. -benchmem -benchtime=2s -timeout 300s ./...
66+
67+
lint:
68+
@command -v golangci-lint >/dev/null 2>&1 && golangci-lint run ./... || echo "golangci-lint not installed, skipping"
69+
70+
# ---------- Cross-platform builds ----------
71+
# These targets build the static library for specific OS/arch combinations.
72+
# Requires appropriate cross-compilation toolchains to be installed.
73+
74+
.PHONY: build-linux-amd64 build-linux-arm64 build-darwin-amd64 build-darwin-arm64 build-windows-amd64
75+
76+
build-linux-amd64:
77+
GOOS=linux GOARCH=amd64 CGO_ENABLED=1 \
78+
CC=x86_64-linux-gnu-gcc CXX=x86_64-linux-gnu-g++ \
79+
$(MAKE) CXX=x86_64-linux-gnu-g++ AR=x86_64-linux-gnu-ar build
80+
81+
build-linux-arm64:
82+
GOOS=linux GOARCH=arm64 CGO_ENABLED=1 \
83+
CC=aarch64-linux-gnu-gcc CXX=aarch64-linux-gnu-g++ \
84+
$(MAKE) CXX=aarch64-linux-gnu-g++ AR=aarch64-linux-gnu-ar build
85+
86+
build-darwin-amd64:
87+
GOOS=darwin GOARCH=amd64 CGO_ENABLED=1 \
88+
$(MAKE) CXXFLAGS="-pthread -std=c++11 $(INCLUDES) -target x86_64-apple-macos11" build
89+
90+
build-darwin-arm64:
91+
GOOS=darwin GOARCH=arm64 CGO_ENABLED=1 \
92+
$(MAKE) CXXFLAGS="-pthread -std=c++11 $(INCLUDES) -target arm64-apple-macos11" build
93+
94+
build-windows-amd64:
95+
GOOS=windows GOARCH=amd64 CGO_ENABLED=1 \
96+
CC=x86_64-w64-mingw32-gcc CXX=x86_64-w64-mingw32-g++ \
97+
$(MAKE) CXX=x86_64-w64-mingw32-g++ AR=x86_64-w64-mingw32-ar \
98+
CXXFLAGS="-std=c++11 $(INCLUDES)" build
99+
100+
# ---------- Cleanup ----------
101+
102+
clean:
103+
ifeq ($(OS),Windows_NT)
104+
-del /Q *.o libhnsw.a *.gcno *.gcda hnsw.exe 2>nul
105+
else
106+
rm -rf *.o libhnsw.a *.gcno *.gcda hnsw
107+
endif
108+
109+
# ---------- Help ----------
110+
111+
help:
112+
@echo "hnswlib-to-go build targets:"
113+
@echo ""
114+
@echo " make build - Build C++ library and Go package (default)"
115+
@echo " make opt - Build with -O3 and -march=native optimizations"
116+
@echo " make portable - Build without -march=native (CI-friendly)"
117+
@echo " make test - Run unit tests"
118+
@echo " make bench - Run benchmarks"
119+
@echo " make lint - Run golangci-lint (if installed)"
120+
@echo " make clean - Remove build artifacts"
121+
@echo ""
122+
@echo "Cross-platform targets (require cross-compilation toolchains):"
123+
@echo " make build-linux-amd64 - Build for Linux x86_64"
124+
@echo " make build-linux-arm64 - Build for Linux aarch64"
125+
@echo " make build-darwin-amd64 - Build for macOS x86_64"
126+
@echo " make build-darwin-arm64 - Build for macOS ARM64"
127+
@echo " make build-windows-amd64 - Build for Windows x86_64 (MinGW)"
128+
@echo ""
129+
@echo "Detected platform: $(UNAME_S) / $(UNAME_M)"

0 commit comments

Comments
 (0)