-
Notifications
You must be signed in to change notification settings - Fork 6
Read the external id map alongside the mmapped CSR #43
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
chishui
merged 2 commits into
opensearch-project:main
from
zirui-song-18:mmap-csr-id-map
Sep 3, 2026
Merged
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -9,7 +9,15 @@ | |
|
|
||
| #include "nsparse/id_map_index.h" | ||
|
|
||
| #include <cstdint> | ||
| #include <filesystem> | ||
| #include <fstream> | ||
| #include <limits> | ||
| #include <memory> | ||
| #include <stdexcept> | ||
| #include <string> | ||
| #include <utility> | ||
| #include <vector> | ||
|
|
||
| #include "nsparse/id_selector.h" | ||
| #include "nsparse/io/index_io.h" | ||
|
|
@@ -78,6 +86,96 @@ void IDMapIndex::add_with_ids(idx_t n, const idx_t* indptr, | |
| external_to_internal_[ids[i]] = old_size + i; | ||
| } | ||
| } | ||
|
|
||
| std::vector<idx_t> IDMapIndex::read_id_file(const char* id_path) { | ||
| check_if_file_valid(id_path, "id map file"); | ||
|
|
||
| std::ifstream in(id_path, std::ios::binary); | ||
| if (!in.is_open()) { | ||
| throw std::runtime_error(std::string("cannot open id map file: ") + | ||
| id_path); | ||
| } | ||
|
|
||
| int64_t count = 0; | ||
| in.read(reinterpret_cast<char*>(&count), sizeof(count)); | ||
| if (!in) { | ||
| throw std::runtime_error(std::string("truncated id map file: ") + | ||
| id_path); | ||
| } | ||
| if (count < 0) { | ||
| throw std::invalid_argument(std::string("negative id map count in: ") + | ||
| id_path); | ||
| } | ||
| const auto map_size = static_cast<size_t>(count); | ||
|
|
||
| // Guard the byte-size arithmetic against wraparound before relying on it. | ||
| if (map_size > (std::numeric_limits<size_t>::max() - sizeof(int64_t)) / | ||
| sizeof(idx_t)) { | ||
| throw std::invalid_argument(std::string("id map count is too large: ") + | ||
| id_path); | ||
| } | ||
|
|
||
| // Reject a truncated or oversized file up front. | ||
| const size_t expected_bytes = sizeof(int64_t) + map_size * sizeof(idx_t); | ||
| if (std::filesystem::file_size(id_path) != expected_bytes) { | ||
| throw std::invalid_argument( | ||
| std::string( | ||
| "id map file size does not match its count (expected ") + | ||
| std::to_string(expected_bytes) + " bytes): " + id_path); | ||
| } | ||
|
|
||
| std::vector<idx_t> internal_to_external(map_size); | ||
| if (map_size > 0) { | ||
| in.read(reinterpret_cast<char*>(internal_to_external.data()), | ||
| static_cast<std::streamsize>(map_size * sizeof(idx_t))); | ||
| if (!in) { | ||
| throw std::runtime_error(std::string("truncated id map file: ") + | ||
| id_path); | ||
| } | ||
| } | ||
| return internal_to_external; | ||
| } | ||
|
|
||
| void IDMapIndex::read_csr_and_ids(const char* csr_path, const char* id_path, | ||
| Residency residency) { | ||
| if (delegate_ == nullptr) { | ||
| throw std::logic_error("IDMapIndex has no delegate index"); | ||
| } | ||
| check_if_file_valid(csr_path, "csr file"); | ||
|
|
||
| // Fully validate and load the id file BEFORE ingesting the CSR, so a | ||
| // missing/malformed/truncated id file leaves this index untouched. Only the | ||
| // count-vs-CSR-row check below can fail once the delegate has ingested; on | ||
| // ANY throw from this method the half-built index must be discarded. | ||
| std::vector<idx_t> internal_to_external = read_id_file(id_path); | ||
|
|
||
| // The id file is known-good; now ingest the vectors (borrowed from the | ||
| // mapping when residency == kMmap). Afterward num_vectors() reflects the | ||
| // CSR rows. | ||
| delegate_->read_csr(csr_path, residency); | ||
|
|
||
| // The map is row-aligned with the CSR, so its count must equal the vectors | ||
| // the delegate just ingested. | ||
| const size_t delegate_size = delegate_->num_vectors(); | ||
| if (internal_to_external.size() != delegate_size) { | ||
| throw std::invalid_argument( | ||
| "id map count (" + std::to_string(internal_to_external.size()) + | ||
| ") does not match the CSR vector count (" + | ||
| std::to_string(delegate_size) + "): " + id_path); | ||
| } | ||
|
|
||
| set_id_map(std::move(internal_to_external)); | ||
| } | ||
|
|
||
| void IDMapIndex::set_id_map(std::vector<idx_t>&& internal_to_external) { | ||
| internal_to_external_ = std::move(internal_to_external); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. from 167-172 is shared logic with other functions, make it a function
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ack |
||
| external_to_internal_.clear(); | ||
| external_to_internal_.reserve(internal_to_external_.size()); | ||
| for (size_t i = 0; i < internal_to_external_.size(); ++i) { | ||
| external_to_internal_[internal_to_external_[i]] = static_cast<idx_t>(i); | ||
| } | ||
| } | ||
|
|
||
| void IDMapIndex::write_index(IOWriter* io_writer) { | ||
| // Write internal_to_external_ vector | ||
| size_t map_size = internal_to_external_.size(); | ||
|
|
@@ -89,23 +187,18 @@ void IDMapIndex::write_index(IOWriter* io_writer) { | |
| nsparse::detail::write_index(delegate_.get(), io_writer, true); | ||
| } | ||
|
|
||
| void IDMapIndex::read_index(IOReader* io_reader, const IndexHeader& header, | ||
| void IDMapIndex::read_index(IOReader* io_reader, const IndexHeader& /*header*/, | ||
| int io_flags) { | ||
| // Read internal_to_external_ vector | ||
| // Read the id map into a local vector, then load the delegate. | ||
| size_t map_size = 0; | ||
| io_reader->read(&map_size, sizeof(size_t), 1); | ||
| internal_to_external_.resize(map_size); | ||
| std::vector<idx_t> internal_to_external(map_size); | ||
| if (map_size > 0) { | ||
| io_reader->read(internal_to_external_.data(), sizeof(idx_t), map_size); | ||
| io_reader->read(internal_to_external.data(), sizeof(idx_t), map_size); | ||
| } | ||
|
|
||
| delegate_.reset(nsparse::detail::read_index(io_reader, true, io_flags)); | ||
|
|
||
| // Rebuild external_to_internal_ from internal_to_external_ | ||
| external_to_internal_.clear(); | ||
| external_to_internal_.reserve(map_size); | ||
| for (size_t i = 0; i < map_size; ++i) { | ||
| external_to_internal_[internal_to_external_[i]] = static_cast<idx_t>(i); | ||
| } | ||
| set_id_map(std::move(internal_to_external)); | ||
| } | ||
| } // namespace nsparse | ||
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
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.
what about moving ids read to a private function and called from here
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.
Ack