Repository navigation
Unified index format V1 #1221
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Jerry Gao (Sanhaoji2)
merged 9 commits into
jegao/LabelHotFix
from
jegao/UnifyIndexFormat
Jul 24, 2026
Merged
Unified index format V1 #1221
Changes from all commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
45a8f75
Unified index format V1
Sanhaoji2 7b9334e
Fix unified index coord padding for non-8-aligned dim
Sanhaoji2 5c07c15
Export unified_index_builder for cross-DLL use
Sanhaoji2 60f0604
Export unified index search factories for cross-DLL use
Sanhaoji2 2746243
Inline simple_bitmask methods; drop dll export to fix C4273
Sanhaoji2 395f534
Minimize simple_bitmask fix: drop dll export, keep out-of-line defs
Sanhaoji2 92021fe
Merge jegao/LabelHotFix into jegao/UnifyIndexFormat
Sanhaoji2 f4ba28b
Fix: don't throw on transiently-empty scratch pool in unified memory …
Sanhaoji2 e8cb201
Fix filtered unified build to use filtered-only candidate exploration
Sanhaoji2 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,96 @@ | ||
| // Copyright (c) Microsoft Corporation. All rights reserved. | ||
| // Licensed under the MIT license. | ||
|
|
||
| #pragma once | ||
|
|
||
| #include <cstdint> | ||
| #include <functional> | ||
| #include <memory> | ||
| #include <optional> | ||
| #include <string> | ||
| #include <vector> | ||
|
|
||
| #include "aligned_file_reader.h" | ||
| #include "distance.h" | ||
| #include "percentile_stats.h" | ||
| #include "unified_index_format.h" | ||
| #include "windows_customizations.h" | ||
|
|
||
| namespace diskann | ||
| { | ||
|
|
||
| struct QueryStats; | ||
| struct DebugTraversalInfo; | ||
|
|
||
| // Knobs passed to unified_index::load. Path identifies the unified container | ||
| // file. `num_threads` and `search_l` size per-thread scratch on the memory | ||
| // implementation. `num_nodes_to_cache` triggers SSD static-cache priming | ||
| // (no-op for the memory implementation). | ||
| struct UnifiedLoadContext | ||
| { | ||
| std::string path; | ||
| uint32_t num_threads = 1; | ||
| uint32_t search_l = 100; | ||
| uint64_t num_nodes_to_cache = 0; | ||
| }; | ||
|
|
||
| // Single in/out container for a search call. The caller fills inputs and | ||
| // allocates the output buffers; search() writes outputs (and optional | ||
| // telemetry) directly. No allocation happens inside search(). | ||
| struct UnifiedSearchContext | ||
| { | ||
| // ---- Inputs ---- | ||
| const void *query = nullptr; // typed by caller as const T* | ||
| size_t K = 10; | ||
| uint32_t L = 100; | ||
| // Filter labels as user-facing strings. Required non-empty if the loaded | ||
| // index has labels; required empty otherwise. The index converts strings | ||
| // to internal label ints per its encoding. | ||
| std::vector<std::string> filter_labels; | ||
| std::optional<uint32_t> beam_width; // SSD-only | ||
| std::optional<uint32_t> io_limit; // SSD-only | ||
| std::function<float(const std::uint8_t *, size_t)> rerank_fn; // SSD-only | ||
|
|
||
| // ---- Outputs (caller-allocated, length >= K) ---- | ||
| uint64_t *indices = nullptr; | ||
| float *distances = nullptr; | ||
|
|
||
| // ---- Optional telemetry sinks (nullptr = no telemetry) ---- | ||
| QueryStats *stats = nullptr; | ||
| DebugTraversalInfo *debug_info = nullptr; | ||
| }; | ||
|
|
||
| // Non-templated public interface returned by the factory. Users program | ||
| // against this; the templated `unified_index_base<T>` implements it. | ||
| class unified_index | ||
| { | ||
| public: | ||
| virtual ~unified_index() = default; | ||
|
|
||
| virtual void load(const UnifiedLoadContext &ctx) = 0; | ||
| virtual void search(UnifiedSearchContext &ctx) = 0; | ||
|
|
||
| virtual const UnifiedIndexHeader &header() const = 0; | ||
| virtual uint64_t num_points() const = 0; | ||
| virtual uint64_t dim() const = 0; | ||
| virtual uint64_t aligned_dim() const = 0; | ||
| virtual diskann::Metric metric() const = 0; | ||
| virtual DataTypeTag data_type() const = 0; | ||
| virtual bool has_labels() const = 0; | ||
|
|
||
| // Resident memory / cardinality accounting for the loaded index, mirroring | ||
| // Index::get_table_stats() and PQFlashIndex::get_table_stats(). | ||
| virtual TableStats get_table_stats() const = 0; | ||
| }; | ||
|
|
||
| // Factory: open a unified file fully in memory. Peeks the 4 KiB header, | ||
| // dispatches on `data_type`, instantiates the right templated implementation, | ||
| // calls load(ctx), returns the owning pointer as the non-templated interface. | ||
| DISKANN_DLLEXPORT std::unique_ptr<unified_index> make_unified_index_memory(const UnifiedLoadContext &ctx); | ||
|
|
||
| // Factory: open a unified file in disk-resident (SSD) mode. The supplied | ||
| // AlignedFileReader is handed to the constructed unified_index_ssd<T>. | ||
| DISKANN_DLLEXPORT std::unique_ptr<unified_index> make_unified_index_ssd( | ||
| std::shared_ptr<AlignedFileReader> reader, const UnifiedLoadContext &ctx); | ||
|
|
||
| } // namespace diskann |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,109 @@ | ||
| // Copyright (c) Microsoft Corporation. All rights reserved. | ||
| // Licensed under the MIT license. | ||
|
|
||
| #pragma once | ||
|
|
||
| #include <cstdint> | ||
| #include <memory> | ||
| #include <string> | ||
|
|
||
| #include "distance.h" | ||
| #include "unified_index.h" | ||
| #include "unified_index_format.h" | ||
| #include "unified_label_data.h" | ||
| #include "unified_node_store.h" | ||
| #include "windows_customizations.h" | ||
|
|
||
| namespace diskann | ||
| { | ||
|
|
||
| class UnifiedIndexReader; | ||
|
|
||
| // Templated implementation of the non-templated `unified_index` interface. | ||
| // Holds the parsed header, the metric, the label data (built by | ||
| // make_unified_label_data), and the node store (a unified_node_store_memory<T> | ||
| // or unified_node_store_ssd<T>, plugged in by the derived class's | ||
| // `load_storage`). | ||
| template <typename T> | ||
| class unified_index_base : public unified_index | ||
| { | ||
| public: | ||
| explicit unified_index_base(diskann::Metric metric); | ||
| ~unified_index_base() override; | ||
|
|
||
| void load(const UnifiedLoadContext &ctx) override; | ||
| void search(UnifiedSearchContext &ctx) override; | ||
|
|
||
| const UnifiedIndexHeader &header() const override | ||
| { | ||
| return _header; | ||
| } | ||
| uint64_t num_points() const override | ||
| { | ||
| return _header.npts; | ||
| } | ||
| uint64_t dim() const override | ||
| { | ||
| return _header.dim; | ||
| } | ||
| uint64_t aligned_dim() const override | ||
| { | ||
| return _header.aligned_dim; | ||
| } | ||
| diskann::Metric metric() const override | ||
| { | ||
| return _metric; | ||
| } | ||
| DataTypeTag data_type() const override | ||
| { | ||
| return data_type_tag_of<T>(); | ||
| } | ||
| bool has_labels() const override | ||
| { | ||
| return _labels && _labels->has_labels(); | ||
| } | ||
| TableStats get_table_stats() const override | ||
| { | ||
| return _table_stats; | ||
| } | ||
|
|
||
| // Templated read-only accessors for in-process callers that *do* know T | ||
| // (unit tests, the index's own search loop). Not on the public interface. | ||
| const unified_label_data_base *labels() const | ||
| { | ||
| return _labels.get(); | ||
| } | ||
| const unified_node_store_base<T> *nodes() const | ||
| { | ||
| return _store.get(); | ||
| } | ||
| unified_node_store_base<T> *nodes() | ||
| { | ||
| return _store.get(); | ||
| } | ||
|
|
||
| protected: | ||
| // Derived class is responsible for instantiating the right _store subclass | ||
| // and calling its load(). It may inspect ctx for SSD-only knobs like | ||
| // ctx.num_nodes_to_cache. | ||
| virtual void load_storage(UnifiedIndexReader &r, const UnifiedLoadContext &ctx) = 0; | ||
| virtual void search_impl(UnifiedSearchContext &ctx) = 0; | ||
|
|
||
| // Fill the storage-specific resident-memory fields (node_mem_usage, | ||
| // graph_mem_usage) of `stats`. Memory reports resident coords/graph; SSD | ||
| // reports the resident PQ codes (graph lives on disk). Called by load() | ||
| // after load_storage() so the store is populated. | ||
| virtual void fill_storage_stats(TableStats &stats) const = 0; | ||
|
|
||
| void validate_header(const UnifiedIndexHeader &h) const; | ||
| void validate_search_context(const UnifiedSearchContext &ctx) const; | ||
|
|
||
| UnifiedIndexHeader _header{}; | ||
| diskann::Metric _metric; | ||
| std::unique_ptr<unified_label_data_base> _labels; // nullptr when header has no labels | ||
| std::unique_ptr<unified_node_store_base<T>> _store; // built by derived load_storage() | ||
| std::string _index_path; | ||
| TableStats _table_stats; | ||
| }; | ||
|
|
||
| } // namespace diskann |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,78 @@ | ||
| // Copyright (c) Microsoft Corporation. All rights reserved. | ||
| // Licensed under the MIT license. | ||
|
|
||
| #pragma once | ||
|
|
||
| #include <cstdint> | ||
| #include <memory> | ||
| #include <string> | ||
|
|
||
| #include "distance.h" | ||
| #include "unified_index_format.h" | ||
| #include "windows_customizations.h" | ||
|
|
||
| namespace diskann | ||
| { | ||
|
|
||
| // All parameters required to build a unified-format index file. | ||
| // | ||
| // One struct, runtime-typed (no template). The data_type field selects which | ||
| // concrete `Index<T>` is instantiated internally; coords are read from | ||
| // `data_file_path` in `.bin` format (the legacy DiskANN file layout). | ||
| struct UnifiedBuildContext | ||
| { | ||
| // --- Input data --- | ||
| std::string data_file_path; // .bin file holding N points x dim coords of `data_type` | ||
| DataTypeTag data_type = DataTypeTag::Float; | ||
| diskann::Metric metric = diskann::Metric::L2; | ||
|
|
||
| // --- Graph build parameters (Vamana) --- | ||
| uint32_t R = 64; // max degree | ||
| uint32_t L = 100; // search list size during build | ||
| float alpha = 1.2f; // pruning alpha | ||
| uint32_t num_threads = 0; // 0 = use omp_get_num_procs() | ||
|
|
||
| // --- PQ parameters --- | ||
| // pq_dim == 0 => no PQ (memory-only unified file; SSD load will reject). | ||
| // 0 < pq_dim < dim => train PQ with `pq_dim` chunks on a sampled subset and | ||
| // emit pivots + codes into the unified file. | ||
| // pq_dim >= dim => train PQ with `dim` chunks (chunk size 1, full-precision | ||
| // per dimension). Clamped so the SSD load path -- which | ||
| // requires HAS_PQ -- can always load the produced file. | ||
| uint32_t pq_dim = 0; | ||
| double pq_sampling_rate = 0.1; // fraction of points to sample for pivot training (clamped server-side) | ||
|
|
||
| // --- Optional filtered-index inputs --- | ||
| std::string label_file; // per-point labels (.txt), empty = unfiltered | ||
| std::string universal_label; // string to treat as "any label" | ||
| bool use_integer_labels = false; | ||
|
|
||
| // --- Output --- | ||
| std::string output_path; // destination unified container file | ||
| }; | ||
|
|
||
| // Builds a unified-format index file end-to-end: trains the Vamana graph from | ||
| // the input data file, optionally trains PQ on a sampled subset, then writes | ||
| // graph + medoids + (optional) PQ + (optional) labels into the unified | ||
| // container at `ctx.output_path`. | ||
| // | ||
| // Class shape (instead of free function) leaves room for future stateful build | ||
| // modes (incremental build, multi-pass, etc.). For now `build()` is the only | ||
| // method. | ||
| // | ||
| // DISKANN_DLLEXPORT so the single public build entry point can be called across | ||
| // the DLL boundary (e.g. from the AdsSnr PublicDiskANNBuilderWrapper). The other | ||
| // unified_* classes remain unexported (internal to the DLL / static lib). | ||
| class DISKANN_DLLEXPORT unified_index_builder | ||
| { | ||
| public: | ||
| unified_index_builder(); | ||
| ~unified_index_builder(); | ||
|
|
||
| // Throws ANNException on failure (file open, mismatched dims, build crash, | ||
| // PQ training error, etc.). Returns successfully when the unified file is | ||
| // fully written and closed. | ||
| void build(const UnifiedBuildContext &ctx); | ||
| }; | ||
|
|
||
| } // namespace diskann |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why we need to include windows_customizations.h here?