-
Notifications
You must be signed in to change notification settings - Fork 22
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
support concurrent read and write for HNSW #178
Conversation
Signed-off-by: jinjiabao.jjb <[email protected]>
Signed-off-by: jinjiabao.jjb <[email protected]>
Signed-off-by: jinjiabao.jjb <[email protected]>
Signed-off-by: jinjiabao.jjb <[email protected]>
Signed-off-by: jinjiabao.jjb <[email protected]>
Signed-off-by: jinjiabao.jjb <[email protected]>
Signed-off-by: jinjiabao.jjb <[email protected]>
Signed-off-by: jinjiabao.jjb <[email protected]>
@@ -211,7 +211,6 @@ TEST_CASE("HNSW Multi-threading", "[ft][hnsw]") { | |||
->Float32Vectors(data.get() + i * dim) | |||
->Owner(false); | |||
auto add_res = index->Add(dataset); | |||
REQUIRE(add_res.has_value()); |
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.
REQUIRE is not thread-safe. If add_res.has_value() is false, then add_res.value().size() will cause a core dump.
@@ -241,7 +240,7 @@ TEST_CASE("HNSW Multi-threading", "[ft][hnsw]") { | |||
|
|||
float recall = correct / max_elements; | |||
std::cout << index->GetStats() << std::endl; | |||
REQUIRE(recall == 1); | |||
REQUIRE(recall > 0.96); |
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.
Previously, it was not built concurrently, so the recall rate could be 1. However, a concurrent build cannot achieve such a high recall rate.
Signed-off-by: jinjiabao.jjb <[email protected]>
int level, | ||
const InnerIdType* neighbors, | ||
size_t neigbor_count) { | ||
linklistsizeint* ll_cur = getLinklistAtLevel(internal_id, level); |
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.
ditto, getLinklistAtLevel
is not thread-safe
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.
This part is read-only, and subsequent modifications will be placed under the protection of the lock
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 happens when one thread reads while another thread modifies ?
I recommend you to complete the revision directly, otherwise the reviewer cannot make a judgment on thread safety
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.
I have added the lock
int level, | ||
InnerIdType neighbor, | ||
size_t max_degree) { | ||
linklistsizeint* ll_cur = getLinklistAtLevel(internal_id, level); |
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.
ditto
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.
ditto
continue; | ||
for (size_t i = 0; i < cand_neighbors.size(); i++) { | ||
auto id = cand_neighbors[i]; | ||
auto& in_edges = getEdges(id, level); |
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.
ditto
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.
ditto
Signed-off-by: jinjiabao.jjb <[email protected]>
Signed-off-by: jinjiabao.jjb <[email protected]>
Signed-off-by: jinjiabao.jjb <[email protected]>
Signed-off-by: jinjiabao.jjb <[email protected]>
Signed-off-by: jinjiabao.jjb <[email protected]>
const InnerIdType* neighbors, | ||
size_t neigbor_count) { | ||
std::unique_lock lock(link_list_locks_[internal_id]); | ||
linklistsizeint* ll_cur = getLinklistAtLevel(internal_id, level); |
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.
getLinklistAtLevel
is thread-safe ?
the sub lock link_list_locks_[internal_id]
can not protect the data_level0_memory_
concurrent access
eg. data_level0_memory_->Resize()
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.
The function getLinklistAtLevel does not access data_level0_memory_; it accesses the graph, while data_level0_memory_ stores the raw data.
InnerIdType neighbor, | ||
size_t max_degree) { | ||
std::unique_lock lock(link_list_locks_[internal_id]); | ||
linklistsizeint* ll_cur = getLinklistAtLevel(internal_id, level); |
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.
ditto
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.
ditto
Signed-off-by: jinjiabao.jjb <[email protected]>
Signed-off-by: jinjiabao.jjb <[email protected]>
Signed-off-by: jinjiabao.jjb <[email protected]>
Codecov ReportAll modified and coverable lines are covered by tests ✅ |
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.
LGTM
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.
LGTM
src/algorithm/hnswlib/hnswalg.h
Outdated
// Locks operations with element by label value | ||
mutable std::shared_mutex resize_mutex_{}; | ||
|
||
mutable std::shared_mutex global_{}; |
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.
global_ could make confused, rename to level_mutex_ or multi_level_mutex_
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.
done
src/algorithm/hnswlib/hnswalg.h
Outdated
mutable std::shared_mutex label_lookup_lock_{}; // lock for label_lookup_ | ||
vsag::UnorderedMap<LabelType, InnerIdType> label_lookup_; |
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.
move these two line up, near other locks
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.
done
src/algorithm/hnswlib/hnswalg.h
Outdated
mutable std::shared_mutex resize_mutex_{}; | ||
|
||
mutable std::shared_mutex global_{}; |
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.
add more comments about the scope of these locks
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.
done
Signed-off-by: jinjiabao.jjb <[email protected]>
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.
lgtm
Signed-off-by: jinjiabao.jjb <[email protected]>
No description provided.