fix: resize Segment data_ to match capacity in ConcurrentArrayInvertedLists#1641
Conversation
…dLists The Segment helper in ConcurrentArrayInvertedLists reserves data_'s capacity but never sizes it, so std::vector::size() stays 0 while operator[] reads and writes at offsets within capacity. That is undefined behavior; on a libstdc++ built with -D_GLIBCXX_ASSERTIONS, operator[] has a bounds check compiled in and the process aborts on the first growing-segment write (e.g. an IVF_FLAT_CC insert). Switch reserve() to resize() so size() == capacity() and operator[] is in-bounds. Same allocation, plus a zero-fill that the subsequent memcpy overwrites; no measurable runtime cost. Behavior on unhardened libstdc++ is unchanged. Signed-off-by: Michael Paul <31833332+MickeyPvX@users.noreply.github.com>
|
Welcome @MickeyPvX! It looks like this is your first PR to zilliztech/knowhere 🎉 |
|
@MickeyPvX 🔍 Important: PR Classification Needed! For efficient project management and a seamless review process, it's essential to classify your PR correctly. Here's how:
For any PR outside the kind/improvement category, ensure you link to the associated issue using the format: “issue: #”. Thanks for your efforts and contribution to the community!. |
|
/kind improvement |
|
/lgtm |
|
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: alexanderguzhva, MickeyPvX The full list of commands accepted by this bot can be found here. The pull request process is described here DetailsNeeds approval from an approver in each of these files:
Approvers can indicate their approval by writing |
Problem
In
thirdparty/faiss/faiss/cppcontrib/knowhere/invlists/InvertedLists.h, theSegmenthelper insideConcurrentArrayInvertedListsreserves capacity in its constructor but never sizes the underlying buffer:After the constructor runs,
data_.size() == 0whiledata_.capacity()issegment_size_ * code_size_. The subsequentoperator[]accesses (directly and via thememcpycalls inadd_entries) read and write at offsets that are within the allocation but pastsize(). Callingstd::vector::operator[]with an index that is not less thansize()is undefined behavior even when the bytes are allocated.What this breaks
On a libstdc++ built with
-D_GLIBCXX_ASSERTIONS(the hardened libstdc++ shipped by several distributions),operator[]has a bounds check compiled in. The first streaming insert into anIVF_FLAT_CCorIndexIVFScalarQuantizerCCcollection trips it and the process aborts:Unhardened libstdc++ compiles
operator[]to plain pointer arithmetic with no check, so the same code runs without complaint.Fix
Use
resize()instead ofreserve()sosize() == capacity()after construction.operator[]is then in-bounds, and thememcpywrites that follow keep working unchanged (they were already assuming the storage existed).resize()does the same allocationreserve()did, plus a zero-fill of the bytes. For the trivialTtypes in use here (uint8_t,idx_t,float) that's a singlememset(0), dominated by the allocation cost. Segment construction is not on the hot path; inserts immediately overwrite the zero bytes viamemcpy.Backport
The same constructor exists on the
v2.5.xandv2.6.xlines at the older paththirdparty/faiss/faiss/invlists/InvertedLists.h(moved tocppcontrib/knowhere/invlists/in #1554). Same one-line change applies on both with offset adjustment.Fixes: #1643