Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
48 changes: 48 additions & 0 deletions NOTICE
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
zvec
Copyright 2025-present the zvec project

This product is licensed under the Apache License, Version 2.0 (see the LICENSE
file). It includes third-party software components that are distributed under
their own licenses, as listed below.

================================================================================
Third-Party Components
================================================================================

--------------------------------------------------------------------------------
pyglass
--------------------------------------------------------------------------------
Project: pyglass — Graph Library for Approximate Similarity Search
Homepage: https://github.com/zilliztech/pyglass
License: MIT License
Used in: src/core/utility/linear_pool.h

The LinearPool implementation (and the accompanying Neighbor / Bitset helpers)
in src/core/utility/linear_pool.h is adapted from pyglass, with modifications
(a BlockHeap-compatible reset()/push_block() interface and the use of
MemoryHelper for huge-page-backed allocation). The related BlockHeap design in
src/core/utility/block_heap.{h,cc} is also derived from pyglass.

Original license text:

MIT License

Copyright (c) 2023 zh Wang

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
99 changes: 98 additions & 1 deletion src/ailego/utility/memory_helper.cc
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
// limitations under the License.

#include "memory_helper.h"
#include <cassert>
#include <cstdio>
#include <cstring>
#include <fstream>
Expand All @@ -29,6 +30,7 @@
#include <mach/mach.h>
#include <sys/sysctl.h>
#endif
#include <sys/mman.h>
#include <unistd.h>
#endif

Expand Down Expand Up @@ -391,5 +393,100 @@ size_t MemoryHelper::HugePageSize(void) {
return page_size;
}

size_t MemoryHelper::AlignHugePageSize(size_t size) {
const size_t page_mask = HugePageSize() - 1;
return (size + page_mask) & (~page_mask);
}

void *MemoryHelper::AllocateHugePage(size_t size, bool zero_fill) {
if (size == 0) {
return nullptr;
}
const size_t aligned_size = AlignHugePageSize(size);

#if defined(_WIN64) || defined(_WIN32)
void *ptr = ::_aligned_malloc(aligned_size, PageSize());
if (ptr == nullptr) {
return nullptr;
}
if (zero_fill) {
std::memset(ptr, 0, aligned_size);
}
return ptr;
#else
void *ptr = ::mmap(nullptr, aligned_size, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (ptr == MAP_FAILED) {
return nullptr;
}
// MADV_HUGEPAGE is a Linux-only hint for transparent huge pages. On
// macOS/BSD (which manage superpages differently) it is intentionally
// absent; skipping it only forgoes a performance hint, not correctness.
#if defined(MADV_HUGEPAGE)
::madvise(ptr, aligned_size, MADV_HUGEPAGE);
#endif
// mmap with MAP_ANONYMOUS already returns zero-filled pages, so an explicit
// memset is only needed when the caller relies on it for a non-anonymous
// fallback; here it is redundant and skipped to avoid touching every page.
(void)zero_fill;
return ptr;
#endif
}

void MemoryHelper::FreeHugePage(void *ptr, size_t size) {
if (ptr == nullptr) {
return;
}
#if defined(_WIN64) || defined(_WIN32)
(void)size;
::_aligned_free(ptr);
#else
::munmap(ptr, AlignHugePageSize(size));
#endif
}

void *MemoryHelper::AllocateAligned(size_t size, size_t alignment,
bool zero_fill) {
assert(alignment != 0 && (alignment & (alignment - 1)) == 0 &&
"alignment must be a power of two");
if (size == 0) {
return nullptr;
}
if (size >= HugePageSize()) {
return AllocateHugePage(size, zero_fill);
}

// Small block: a regular aligned allocation avoids reserving a whole huge
// page. std::aligned_alloc requires the size to be a multiple of alignment.
const size_t aligned_size = (size + alignment - 1) / alignment * alignment;
#if defined(_WIN64) || defined(_WIN32)
void *ptr = ::_aligned_malloc(aligned_size, alignment);
#else
void *ptr = std::aligned_alloc(alignment, aligned_size);
#endif
if (ptr == nullptr) {
return nullptr;
}
if (zero_fill) {
std::memset(ptr, 0, aligned_size);
}
return ptr;
}

void MemoryHelper::FreeAligned(void *ptr, size_t size) {
if (ptr == nullptr) {
return;
}
if (size >= HugePageSize()) {
FreeHugePage(ptr, size);
return;
}
#if defined(_WIN64) || defined(_WIN32)
::_aligned_free(ptr);
#else
std::free(ptr);
#endif
}

} // namespace ailego
} // namespace zvec
} // namespace zvec
59 changes: 59 additions & 0 deletions src/ailego/utility/memory_helper.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,65 @@ struct MemoryHelper {
//! Retrieve the huge page size of memory
static size_t HugePageSize(void);

//! Round `size` up to a multiple of the huge page size.
static size_t AlignHugePageSize(size_t size);

//! Allocate a large, page-aligned block that prefers transparent huge pages.
//!
//! On Linux the block is obtained via anonymous mmap and hinted with
//! MADV_HUGEPAGE; on other platforms it falls back to a page-aligned
//! allocation without the huge-page hint (which is a performance hint, not a
//! correctness requirement). Returns nullptr on failure.
//!
//! `size` is rounded up to the huge page size internally, and the same
//! rounded value is what the corresponding FreeHugePage call expects, so
//! callers should treat the returned block as exactly AlignHugePageSize(size)
//! bytes.
//!
//! `zero_fill` requests zeroed memory: when true the returned block is
//! guaranteed to be zero-initialized. When false the caller does not require
//! zeroing, but the implementation is still free to return zeroed memory and
//! does so on the anonymous-mmap path (MAP_ANONYMOUS pages are always zero),
//! where an explicit memset is skipped to preserve lazy paging. In other
//! words, true => always zeroed; false => zeroing is not guaranteed either
//! way. Never assume non-zero contents.
//!
//! Blocks returned here MUST be released with FreeHugePage (never free()),
//! because the underlying allocator differs per platform.
static void *AllocateHugePage(size_t size, bool zero_fill = true);

//! Release a block previously returned by AllocateHugePage.
//!
//! `size` must be the same value originally passed to AllocateHugePage; it is
//! required because the Linux mmap path needs the length for munmap.
static void FreeHugePage(void *ptr, size_t size);

//! Allocate an aligned block, choosing the backing allocator by size.
//!
//! When `size` is at least the huge page size, the block is obtained via
//! AllocateHugePage (huge-page-backed, page-aligned). Otherwise a regular
//! `alignment`-aligned allocation is used, which avoids wasting a full huge
//! page on small buffers. Returns nullptr on failure.
//!
//! `alignment` must be a power of two.
//!
//! `zero_fill` follows the same contract as AllocateHugePage: true guarantees
//! zeroed memory; false does not require zeroing but the implementation may
//! still return zeroed memory (it does on the huge-page mmap path). Never
//! assume non-zero contents.
//!
//! Blocks returned here MUST be released with FreeAligned, passing the same
//! `size`, because the chosen allocator (and therefore the matching free) is
//! derived from `size`.
static void *AllocateAligned(size_t size, size_t alignment = 64,
bool zero_fill = true);

//! Release a block previously returned by AllocateAligned.
//!
//! `size` must be the same value originally passed to AllocateAligned so the
//! same allocator path is selected for releasing the block.
static void FreeAligned(void *ptr, size_t size);

//! Retrieve the VSZ and RSS of self process in bytes
static bool SelfUsage(size_t *vsz, size_t *rss);

Expand Down
21 changes: 21 additions & 0 deletions src/core/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,27 @@ if(RABITQ_SUPPORTED AND AUTO_DETECT_ARCH)
endforeach()
endif()

# utility/block_heap.cc uses AVX2 intrinsics guarded by __AVX2__. When the
# host toolchain supports it, compile this source with an AVX2-capable
# -march so AVX2 codegen is emitted. zvec_core glob-collects this source
# too, so per-file flags must be set here as well (in addition to the
# core_utility target in utility/CMakeLists.txt). Callers runtime-gate
# invocation of BlockHeap paths on CpuFeatures::AVX2.
if(NOT ANDROID AND AUTO_DETECT_ARCH)
if(HOST_ARCH MATCHES "^(x86|x64)$")
setup_compiler_march_for_x86(
_BLOCK_HEAP_MARCH_SSE _BLOCK_HEAP_MARCH_AVX2
_BLOCK_HEAP_MARCH_AVX512 _BLOCK_HEAP_MARCH_AVX512FP16)
if(_BLOCK_HEAP_MARCH_AVX2)
set_source_files_properties(
utility/block_heap.cc
PROPERTIES
COMPILE_FLAGS "${_BLOCK_HEAP_MARCH_AVX2}"
)
endif()
endif()
endif()

cc_directory(framework)
cc_directory(algorithm)
cc_directory(metric)
Expand Down
2 changes: 1 addition & 1 deletion src/core/algorithm/hnsw/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ cc_library(
NAME core_knn_hnsw
STATIC SHARED STRICT ALWAYS_LINK
SRCS *.cc
LIBS core_framework sparsehash
LIBS core_framework core_utility sparsehash
INCS . ${PROJECT_ROOT_DIR}/src/core ${PROJECT_ROOT_DIR}/src/core/algorithm
LDFLAGS "${CORE_KNN_HNSW_LDFLAGS}"
VERSION "${PROXIMA_ZVEC_VERSION}"
Expand Down
Loading
Loading