Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
97 commits
Select commit Hold shift + click to select a range
85f89dc
fix
iaojnh May 20, 2026
9e58670
Merge branch 'main' into fix/exclude-arrow
iaojnh May 20, 2026
46bf9aa
Merge branch 'alibaba:main' into main
iaojnh May 21, 2026
d6547aa
Merge branch 'alibaba:main' into main
iaojnh May 25, 2026
c9a4d94
Merge branch 'alibaba:main' into main
iaojnh May 26, 2026
5bff8b6
Merge branch 'alibaba:main' into main
iaojnh May 29, 2026
10f1dee
Merge branch 'alibaba:main' into main
iaojnh Jun 1, 2026
ad6ac3a
Merge branch 'alibaba:main' into main
iaojnh Jun 2, 2026
5c045b0
Merge branch 'alibaba:main' into main
iaojnh Jun 3, 2026
570da53
Merge branch 'alibaba:main' into main
iaojnh Jun 8, 2026
d3fecea
Merge branch 'alibaba:main' into main
iaojnh Jun 9, 2026
4f07741
Merge branch 'alibaba:main' into main
iaojnh Jun 10, 2026
9e900c5
Merge branch 'alibaba:main' into main
iaojnh Jun 11, 2026
4761640
Merge branch 'alibaba:main' into main
iaojnh Jun 15, 2026
a842e22
Merge branch 'alibaba:main' into main
iaojnh Jun 15, 2026
8bb7e53
Merge branch 'alibaba:main' into main
iaojnh Jun 17, 2026
d9af524
Merge branch 'alibaba:main' into main
iaojnh Jun 22, 2026
973d233
Merge branch 'alibaba:main' into main
iaojnh Jun 24, 2026
74da77a
refactor: use dlopen
richyreachy Jun 24, 2026
43cb151
Merge branch 'alibaba:main' into main
iaojnh Jun 25, 2026
17a7fd5
refactor: use dlopen to load libaio
richyreachy Jun 25, 2026
1dd03d4
feat: add diskann windows
richyreachy Jun 25, 2026
82d08f6
fix: support io uring
richyreachy Jun 25, 2026
8ef5d2d
fix: log out the warning only once
richyreachy Jun 26, 2026
813f2ab
fix: warning
richyreachy Jun 26, 2026
50c6c8d
fix: add log
richyreachy Jun 26, 2026
0cf5cdd
fix: fix conflict
richyreachy Jun 26, 2026
ceee226
fix: add log
richyreachy Jun 26, 2026
5e77ee3
Merge branch 'refactor/diskann_dlopen' of github.com:richyreachy/zvec…
richyreachy Jun 26, 2026
34066a7
Merge branch 'refactor/diskann_dlopen' into feat/diskann_on_iouring
richyreachy Jun 26, 2026
29b59b9
fix: remove log
richyreachy Jun 26, 2026
6d774ed
refactor: merge main
richyreachy Jun 26, 2026
4685c96
Merge branch 'alibaba:main' into main
iaojnh Jun 30, 2026
e2a2ede
Merge branch 'main' into refactor/diskann_dlopen
richyreachy Jun 30, 2026
a2524eb
fix: fix windows build
richyreachy Jul 1, 2026
ac109ae
Merge branch 'refactor/diskann_dlopen' of github.com:richyreachy/zvec…
richyreachy Jul 1, 2026
6688e1a
fix: remove libaio dependency
richyreachy Jul 1, 2026
837a39d
Merge branch 'refactor/diskann_dlopen' into feat/diskann_on_iouring
richyreachy Jul 2, 2026
5d63ac0
merge from io uring
richyreachy Jul 2, 2026
5d3b96a
fix: fix
richyreachy Jul 2, 2026
6cb054a
fix: fix makefile
richyreachy Jul 2, 2026
760fb7a
Merge branch 'main' into refactor/diskann_dlopen
richyreachy Jul 2, 2026
f2559b5
fix: remove no use code
richyreachy Jul 2, 2026
d753080
fix: fix makefile
richyreachy Jul 2, 2026
bc7b843
Merge branch 'refactor/diskann_dlopen' of github.com:richyreachy/zvec…
richyreachy Jul 2, 2026
c5ca726
fix: support windows
richyreachy Jul 3, 2026
c7f34c2
fix: fix windows
richyreachy Jul 3, 2026
77a7b4d
fix: fix windows
richyreachy Jul 3, 2026
292322c
fix: fix ut
richyreachy Jul 3, 2026
3f59037
fix: fix reader
richyreachy Jul 3, 2026
a0a9ce9
fix: remove export map
richyreachy Jul 3, 2026
37780e7
fix: foramt
richyreachy Jul 3, 2026
ba9be50
fix: fix unused
richyreachy Jul 3, 2026
69b1e23
fix: clean code
richyreachy Jul 3, 2026
081aee3
fix: add ci job to test libaio
richyreachy Jul 3, 2026
ad07197
fix: fix type
richyreachy Jul 3, 2026
ce93b16
fix: fix array
richyreachy Jul 3, 2026
ab8901a
fix: fix on comment
richyreachy Jul 6, 2026
63f5ff7
fix: remove diskann runtime
richyreachy Jul 6, 2026
0bb0a61
fix: fix read
richyreachy Jul 6, 2026
58e5f93
Merge branch 'feat/diskann_on_windows' of github.com:richyreachy/zvec…
richyreachy Jul 6, 2026
1954039
Merge branch 'refactor/diskann_dlopen' into feat/diskann_on_iouring
richyreachy Jul 7, 2026
5ffc23a
refactor: move libaio loader to ailego
richyreachy Jul 7, 2026
1c7ff60
fix: add io backend
richyreachy Jul 7, 2026
54eaa11
fix: add io backend
richyreachy Jul 7, 2026
9870923
Merge branch 'main' into refactor/diskann_dlopen
richyreachy Jul 7, 2026
f19defb
Merge branch 'main' into refactor/diskann_dlopen
richyreachy Jul 7, 2026
34ae23b
merge with dlopen via libaio
richyreachy Jul 8, 2026
4b4a00b
fix: fix
richyreachy Jul 8, 2026
241dd7a
Merge branch 'main' into refactor/diskann_dlopen
richyreachy Jul 8, 2026
e1048c0
fix: remove ci
richyreachy Jul 8, 2026
fde7813
fix: add ci
richyreachy Jul 8, 2026
6496a0b
Merge branch 'refactor/diskann_dlopen' of github.com:richyreachy/zvec…
richyreachy Jul 8, 2026
4567d99
Merge branch 'main' into refactor/diskann_dlopen
richyreachy Jul 8, 2026
ca89c30
fix: debug output
richyreachy Jul 8, 2026
d63cb21
Merge branch 'refactor/diskann_dlopen' into feat/diskann_on_iouring
richyreachy Jul 8, 2026
34a4cad
fix: fix merge
richyreachy Jul 8, 2026
0583231
fix: fix ut
richyreachy Jul 8, 2026
0afa1d5
fix: fix ut
richyreachy Jul 8, 2026
69f41bd
Merge branch 'feat/diskann_on_iouring' into feat/diskann_on_windows
richyreachy Jul 8, 2026
232e72a
fix: fix
richyreachy Jul 8, 2026
bf4f630
fix: fix ut
richyreachy Jul 8, 2026
31c0fe5
fix: schema
richyreachy Jul 9, 2026
a07dd9d
Merge branch 'alibaba:main' into main
iaojnh Jul 10, 2026
229dc57
Merge branch 'alibaba:main' into main
iaojnh Jul 13, 2026
9ac71b2
Merge branch 'alibaba:main' into main
iaojnh Jul 15, 2026
23a4354
Merge branch 'alibaba:main' into main
iaojnh Jul 17, 2026
a1ad5fc
Merge branch 'alibaba:main' into main
iaojnh Jul 20, 2026
ef12c90
Merge branch 'alibaba:main' into main
iaojnh Jul 23, 2026
95d9225
Merge branch 'alibaba:main' into main
iaojnh Jul 27, 2026
f87657d
Merge PR #582: support DiskANN on Windows
iaojnh Jul 27, 2026
103c688
add windows support
iaojnh Jul 27, 2026
28ecbcb
optimize
iaojnh Jul 28, 2026
76e41c4
Merge branch 'alibaba:main' into main
iaojnh Jul 28, 2026
76bfce3
Merge branch 'main' into feat/diskann-support-windows
iaojnh Jul 28, 2026
23fce89
format
iaojnh Jul 29, 2026
d1ed35e
clang format
iaojnh Jul 29, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -122,14 +122,15 @@ else()
endif()
message(STATUS "RABITQ_ARCH_FLAG: ${RABITQ_ARCH_FLAG}")

# DiskAnn support (Linux x86_64 only; libaio loaded at runtime via dlopen)
if(CMAKE_SYSTEM_NAME STREQUAL "Linux" AND CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|i686|i386" AND NOT ANDROID AND NOT IOS)
# DiskAnn support (Linux or Windows x86/x86_64).
if((CMAKE_SYSTEM_NAME STREQUAL "Linux" AND CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|i686|i386" AND NOT ANDROID AND NOT IOS)
OR (WIN32 AND CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|AMD64|amd64|i686|i386"))
set(DISKANN_SUPPORTED ON)
add_definitions(-DDISKANN_SUPPORTED=1)
else()
set(DISKANN_SUPPORTED OFF)
add_definitions(-DDISKANN_SUPPORTED=0)
message(STATUS "DiskAnn support disabled - only supported on Linux x86_64")
message(STATUS "DiskAnn support disabled - only supported on Linux or Windows x86/x86_64")
endif()
message(STATUS "DISKANN_SUPPORTED: ${DISKANN_SUPPORTED}")

Expand Down
5 changes: 4 additions & 1 deletion examples/c++/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@ get_filename_component(ZVEC_ROOT_DIR "${CMAKE_CURRENT_LIST_DIR}/../.." ABSOLUTE)
set(ZVEC_INCLUDE_DIR ${ZVEC_ROOT_DIR}/src/include)
set(ZVEC_LIB_DIR ${ZVEC_ROOT_DIR}/${HOST_BUILD_DIR}/lib)

include_directories(${ZVEC_INCLUDE_DIR})
# Add include and library search paths
# ${ZVEC_ROOT_DIR}/src is needed because public headers under src/include/
# reference ailego/ headers (e.g. <ailego/io/io_backend.h>) that live in src/.
include_directories(${ZVEC_INCLUDE_DIR} ${ZVEC_ROOT_DIR}/src)
set(ZVEC_LIB_SEARCH_DIRS ${ZVEC_LIB_DIR})

# Support multi-config builds (MSVC puts libs in Debug/Release subdirectories)
Expand Down
9 changes: 7 additions & 2 deletions python/tests/detail/fixture_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@
import logging
import platform

DISKANN_SUPPORTED = platform.system() == "Linux" and platform.machine() in (
DISKANN_SUPPORTED = platform.system() in (
"Linux",
"Windows",
) and platform.machine() in (
"x86_64",
"AMD64",
"i686",
Expand All @@ -27,7 +30,9 @@ def _ensure_diskann_runtime_or_reason() -> str | None:
_DISKANN_PRELOAD_DONE = True

if not DISKANN_SUPPORTED:
_DISKANN_PRELOAD_REASON = "DiskAnn only supported on Linux x86_64"
_DISKANN_PRELOAD_REASON = (
"DiskAnn is only supported on Linux or Windows x86/x86_64"
)
return _DISKANN_PRELOAD_REASON

_DISKANN_PRELOAD_REASON = None
Expand Down
19 changes: 11 additions & 8 deletions python/tests/test_collection_diskann.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@

Mirrors ``test_collection_hnsw_rabitq.py`` but targets the DiskAnn index.

Two platform-level prerequisites are enforced at module import time:
Platform prerequisites are enforced at module import time:

1. DiskAnn is currently built only for Linux x86_64 — other platforms are
1. DiskAnn is built for Linux and Windows x86/x86_64; other platforms are
skipped wholesale.
2. libaio is loaded eagerly (via dlopen) inside DiskAnnBuilder::init() /
2. On Linux, libaio is loaded eagerly (via dlopen) inside DiskAnnBuilder::init() /
DiskAnnStreamer::init(). If libaio is missing, DiskAnn falls back to
synchronous pread() — the tests still run but with degraded performance.

Expand All @@ -39,8 +39,11 @@
# Platform gating (must happen BEFORE we touch zvec).
# --------------------------------------------------------------------------- #
pytestmark = pytest.mark.skipif(
not (sys.platform == "linux" and platform.machine() in ("x86_64", "AMD64")),
reason="DiskAnn plugin is only supported on Linux x86_64",
not (
sys.platform in ("linux", "win32")
and platform.machine() in ("x86_64", "AMD64", "i686", "i386")
),
reason="DiskAnn is only supported on Linux or Windows x86/x86_64",
)

import zvec # noqa: E402
Expand Down Expand Up @@ -524,10 +527,10 @@ def test_optimize_close_reopen_and_query(
collection_option,
multiple_docs: list[Doc],
):
"""Test inserting 100 docs, optimize, close, reopen and query."""
# Create collection and insert 100 documents
"""Test a UTF-8 path through optimize, close, reopen and query."""
# A non-ASCII path exercises CreateFileW in the Windows DiskAnn reader.
temp_dir = tmp_path_factory.mktemp("zvec_diskann_optimize")
collection_path = temp_dir / "test_optimize_collection"
collection_path = temp_dir / "磁盘索引_テスト"

coll = zvec.create_and_open(
path=str(collection_path),
Expand Down
17 changes: 16 additions & 1 deletion python/tests/test_typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@
# limitations under the License.
from __future__ import annotations

import sys

import pytest
import zvec
from zvec import (
DataType,
IndexType,
Expand All @@ -34,6 +37,8 @@
(DataType.FLOAT, "FLOAT"),
(IndexType.HNSW, "HNSW"),
(IOBackendType.PREAD, "PREAD"),
(IOBackendType.IO_URING, "IO_URING"),
(IOBackendType.WINDOWS_OVERLAPPED, "WINDOWS_OVERLAPPED"),
(MetricType.COSINE, "COSINE"),
(QuantizeType.INT8, "INT8"),
(StatusCode.OK, "OK"),
Expand All @@ -49,6 +54,8 @@ def test_enum_names(member, name):
(DataType.FLOAT, 8),
(IndexType.HNSW, 1),
(IOBackendType.PREAD, 0),
(IOBackendType.IO_URING, 2),
(IOBackendType.WINDOWS_OVERLAPPED, 3),
(MetricType.COSINE, 3),
(QuantizeType.INT8, 2),
(StatusCode.OK, 0),
Expand Down Expand Up @@ -112,11 +119,19 @@ def test_index_type_has_member(member):
assert member in IndexType.__members__


@pytest.mark.parametrize("member", ["PREAD", "LIBAIO"])
@pytest.mark.parametrize(
"member", ["PREAD", "LIBAIO", "IO_URING", "WINDOWS_OVERLAPPED"]
)
def test_io_backend_type_has_member(member):
assert member in IOBackendType.__members__


@pytest.mark.skipif(sys.platform != "win32", reason="Windows-specific backend")
def test_windows_io_backend_type():
assert zvec.io_backend_type() == IOBackendType.WINDOWS_OVERLAPPED
assert "overlapped" in zvec.io_backend_description().lower()


@pytest.mark.parametrize("member", ["FP16", "INT8", "INT4", "UNDEFINED"])
def test_quantize_type_has_member(member):
assert member in QuantizeType.__members__
Expand Down
7 changes: 3 additions & 4 deletions python/zvec/__init__.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,11 @@ from .zvec import create_and_open, init, open
def io_backend_type() -> IOBackendType:
"""Returns the current I/O backend type for DiskAnn async disk reads
as an IOBackendType enum (zvec.typing.IOBackendType).
IOBackendType.LIBAIO if libaio is available, IOBackendType.PREAD otherwise."""
Linux selects IO_URING, LIBAIO, then PREAD; Windows uses
WINDOWS_OVERLAPPED."""

def io_backend_description() -> str:
"""Returns a human-readable description of the current I/O backend.
When only pread is available, includes instructions for installing
libaio to enable async I/O."""
"""Returns a platform-specific description of the current I/O backend."""

def set_default_jieba_dict_dir(dir: str) -> None:
"""Register the process-wide default jieba dict directory."""
Expand Down
12 changes: 11 additions & 1 deletion python/zvec/typing/__init__.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,8 @@ class IOBackendType:

- PREAD: Synchronous pread() — no async I/O.
- LIBAIO: libaio loaded at runtime via dlopen().
- IO_URING: io_uring accessed directly through the Linux kernel ABI.
- WINDOWS_OVERLAPPED: Windows overlapped I/O using I/O completion ports.

Examples:
>>> from zvec.typing import IOBackendType
Expand All @@ -142,13 +144,21 @@ class IOBackendType:
PREAD

LIBAIO

IO_URING

WINDOWS_OVERLAPPED
"""

LIBAIO: typing.ClassVar[IOBackendType] # value = <IOBackendType.LIBAIO: 1>
IO_URING: typing.ClassVar[IOBackendType] # value = <IOBackendType.IO_URING: 2>
PREAD: typing.ClassVar[IOBackendType] # value = <IOBackendType.PREAD: 0>
WINDOWS_OVERLAPPED: typing.ClassVar[
IOBackendType
] # value = <IOBackendType.WINDOWS_OVERLAPPED: 3>
__members__: typing.ClassVar[
dict[str, IOBackendType]
] # value = {'PREAD': <IOBackendType.PREAD: 0>, 'LIBAIO': <IOBackendType.LIBAIO: 1>}
] # value includes PREAD, LIBAIO, IO_URING, and WINDOWS_OVERLAPPED

def __eq__(self, other: typing.Any) -> bool: ...
def __getstate__(self) -> int: ...
Expand Down
18 changes: 18 additions & 0 deletions src/ailego/io/io_backend.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Copyright 2025-present the zvec project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#pragma once

// Compatibility include for internal users of the selector.
#include <ailego/io/io_backend_def.h>
72 changes: 65 additions & 7 deletions src/ailego/io/io_backend_def.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,41 +29,61 @@

#pragma once

#include <cstring>
#include <ailego/io/iouring_def.h>
#include <ailego/io/libaio_loader.h>
#include <zvec/ailego/io/io_backend.h>

#if defined(__linux) || defined(__linux__)
#include <unistd.h>
#endif

namespace zvec {
namespace ailego {

// Returns a human-readable name for the given backend type.
inline const char *IOBackendTypeName(IOBackendType type) {
switch (type) {
case IOBackendType::kIoUring:
return "io_uring";
case IOBackendType::kLibAio:
return "libaio";
case IOBackendType::kPread:
return "pread";
case IOBackendType::kWindowsOverlapped:
return "windows_overlapped";
}
return "unknown";
}

// Returns a human-readable description for the given backend type.
// When the backend is kPread, includes installation guidance for libaio.
// Returns a platform-specific human-readable backend description.
inline const char *IOBackendDescription(IOBackendType type) {
switch (type) {
case IOBackendType::kIoUring:
return "io_uring async I/O backend accessed through the Linux kernel "
"ABI.";
case IOBackendType::kLibAio:
return "libaio async I/O backend loaded at runtime via dlopen().";
case IOBackendType::kPread:
#if defined(__linux) || defined(__linux__)
return "No async I/O backend available. Install libaio (e.g. "
"'apt-get install libaio1', or 'libaio1t64' on Ubuntu 24.04+) "
"and retry. DiskAnn will fall back to synchronous pread() \u2014 "
"performance will be degraded.";
#else
return "Synchronous pread I/O backend.";
#endif
case IOBackendType::kWindowsOverlapped:
return "Windows unbuffered overlapped I/O backend using I/O completion "
"ports.";
}
return "Unknown I/O backend.";
}

// Singleton that loads and queries an I/O backend on demand.
//
// available() (no arg) tries the best backend with priority (libaio > pread)
// available() (no arg) tries the best backend with priority
// (io_uring > libaio > pread)
// and returns the loaded backend type.
// available(IOBackendType) tries a specific backend.
// Use type() / name() to query the loaded backend without triggering a load.
Expand All @@ -74,24 +94,49 @@ class IOBackend {
return instance;
}

// Try to load the best available backend (libaio > pread).
// Select Windows overlapped I/O, or the best available Linux backend
// (io_uring > libaio > pread).
// Returns the loaded backend type.
// Idempotent — if already loaded, returns immediately.
IOBackendType available() {
#if defined(_WIN32) || defined(_WIN64)
type_ = IOBackendType::kWindowsOverlapped;
return type_;
#else
if (type_ != IOBackendType::kPread) {
return type_;
}
return available(IOBackendType::kLibAio);
IOBackendType type = available(IOBackendType::kIoUring);
if (type == IOBackendType::kPread) {
type = available(IOBackendType::kLibAio);
}
return type;
#endif
}

// Try to load the requested backend. Returns the loaded backend type
// (may differ from requested if the load failed — falls back to kPread).
// Try to load the requested backend. On Linux, failed requests fall back to
// kPread. Windows always reports its native overlapped-I/O backend.
// Idempotent — if the same backend is already loaded, returns immediately.
IOBackendType available(IOBackendType requested) {
#if defined(_WIN32) || defined(_WIN64)
(void)requested;
type_ = IOBackendType::kWindowsOverlapped;
return type_;
#else
if (type_ == requested && type_ != IOBackendType::kPread) {
return type_;
}
#if defined(__linux) || defined(__linux__)
if (requested == IOBackendType::kIoUring) {
struct io_uring_params params;
std::memset(&params, 0, sizeof(params));
int fd = static_cast<int>(::syscall(__NR_io_uring_setup, 1, &params));
if (fd >= 0) {
::close(fd);
type_ = IOBackendType::kIoUring;
return type_;
}
}
if (requested == IOBackendType::kLibAio) {
if (LibAioLoader::Instance().load() &&
LibAioLoader::Instance().is_available()) {
Expand All @@ -102,6 +147,7 @@ class IOBackend {
#endif
type_ = IOBackendType::kPread;
return type_;
#endif
}

bool is_pread() {
Expand All @@ -112,6 +158,14 @@ class IOBackend {
return available() == IOBackendType::kLibAio;
}

bool is_iouring() {
return available() == IOBackendType::kIoUring;
}

bool is_windows_overlapped() {
return available() == IOBackendType::kWindowsOverlapped;
}

// Returns the loaded backend type.
IOBackendType type() const {
return type_;
Expand All @@ -130,7 +184,11 @@ class IOBackend {
private:
IOBackend() = default;

#if defined(_WIN32) || defined(_WIN64)
IOBackendType type_{IOBackendType::kWindowsOverlapped};
#else
IOBackendType type_{IOBackendType::kPread};
#endif
};

} // namespace ailego
Expand Down
Loading
Loading