Commit 1e69d5a
Fix peak-memory spike when loading IVF invlists via IO_FLAG_MMAP_IFC (#5122)
Summary:
On a 148 GB `IndexIVFRaBitQ`, `faiss.read_index(path, IO_FLAG_MMAP_IFC)` currently spikes to **165 GB of committed private heap** for ~50 seconds during load, before dropping back to the intended ~9 GB steady state. The spike is transient but matches the entire invlist payload, which defeats the purpose of `IO_FLAG_MMAP_IFC` (keep invlist codes and ids file-cache-backed, not pagefile-committed).
Root cause is a two-loop pattern in `read_InvertedLists_up` (`faiss/impl/index_read.cpp`) for both the `ilar` (`ArrayInvertedLists`) and `ilpn` (`ArrayInvertedListsPanorama`) branches:
```cpp
// Loop A: pre-allocate every list's owning std::vector (= full invlist size on heap)
for (i = 0 .. nlist) {
ails->codes[i].resize(sizes[i] * code_size);
ails->ids[i].resize(sizes[i]);
}
// Loop B: replace each owning vector with a view into mmap
for (i = 0 .. nlist) {
read_vector_with_known_size(ails->codes[i], ...);
read_vector_with_known_size(ails->ids[i], ...);
}
```
`read_vector_with_known_size` detects a `MappedFileIOReader` and replaces the target `MaybeOwnedVector` via `create_view`, correctly releasing the owning storage. But Loop A has already committed 100% of the invlist size to the heap before Loop B can release any of it.
## Fix
Merge the two loops into a single pass per list, so each list's owning heap allocation is released via the view-substitution before the next list's is made. Peak heap during load is now bounded by a single list's worth (~one nprobe cluster, typically under 1 MB), not the sum of all lists'.
Applied to both the `ilar` and `ilpn` branches. End state is byte-identical: with `MappedFileIOReader` every `MaybeOwnedVector` ends up as a view; with a regular `FileIOReader` every `MaybeOwnedVector` ends up as owning storage.
## Measured impact
Same IndexIVFRaBitQ file (IVF262144_HNSW32,RaBitQ8, 99.7 M vectors, nlist=262144, code_size=1556, 148 GB on disk), loaded via `faiss.read_index(path, IO_FLAG_MMAP_IFC)`:
| Metric | Before | After |
|---------------------|--------:|------:|
| Peak private memory | 164 GB | 10 GB |
| Load time | 99 s | 16 s |
| Steady-state RSS | 9 GB | 9 GB |
Load time drops because ~90 s of the original was spent in `std::vector::resize` zero-filling pages that were about to be discarded.
Pull Request resolved: #5122
Test Plan:
- [x] Manual: `faiss.read_index(path, IO_FLAG_MMAP_IFC)` on the 148 GB production index; `search(q, 20)` at `nprobe=64` returns the same top-k ids before and after the patch.
- [x] Manual: 1 Hz private-memory polling during a 10-query, `k=500`, `nprobe=256` stress — RSS grows identically to the pre-patch behaviour (file-cache page-fault growth, not private heap), confirming the view substitution still works.
- [ ] Existing FAISS CI — no new tests added. The bug is a transient peak that existing tests cannot observe, and the fix is an ordering change with no new code path.
## Safety
- No API, ABI, serialization-format, or behavioural change.
- `FAISS_CHECK_DESERIALIZATION_LOOP_LIMIT` hardening still runs in the same place, before the merged loop.
- Works identically on `FileIOReader` (heap-owning end state) and `MappedFileIOReader` (view end state).
Made with [Cursor](https://cursor.com)
Reviewed By: mdouze
Differential Revision: D103055438
Pulled By: mnorris11
fbshipit-source-id: 5cdc371aa78547f00a4e8e854cac84945d81b6b51 parent 28b2b66 commit 1e69d5a
2 files changed
Lines changed: 52 additions & 17 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
518 | 518 | | |
519 | 519 | | |
520 | 520 | | |
| 521 | + | |
| 522 | + | |
521 | 523 | | |
522 | 524 | | |
| 525 | + | |
523 | 526 | | |
524 | | - | |
| 527 | + | |
525 | 528 | | |
526 | 529 | | |
527 | 530 | | |
528 | | - | |
529 | | - | |
530 | | - | |
| 531 | + | |
| 532 | + | |
| 533 | + | |
531 | 534 | | |
532 | 535 | | |
533 | 536 | | |
| |||
549 | 552 | | |
550 | 553 | | |
551 | 554 | | |
552 | | - | |
553 | | - | |
554 | | - | |
555 | 555 | | |
556 | 556 | | |
557 | 557 | | |
| |||
627 | 627 | | |
628 | 628 | | |
629 | 629 | | |
| 630 | + | |
| 631 | + | |
| 632 | + | |
| 633 | + | |
| 634 | + | |
630 | 635 | | |
631 | | - | |
| 636 | + | |
| 637 | + | |
632 | 638 | | |
633 | | - | |
| 639 | + | |
634 | 640 | | |
635 | 641 | | |
636 | 642 | | |
637 | | - | |
638 | | - | |
639 | | - | |
640 | | - | |
| 643 | + | |
| 644 | + | |
| 645 | + | |
| 646 | + | |
641 | 647 | | |
642 | 648 | | |
643 | 649 | | |
644 | 650 | | |
645 | 651 | | |
646 | 652 | | |
647 | 653 | | |
648 | | - | |
649 | | - | |
650 | | - | |
651 | 654 | | |
652 | 655 | | |
653 | 656 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
574 | 574 | | |
575 | 575 | | |
576 | 576 | | |
577 | | - | |
| 577 | + | |
| 578 | + | |
| 579 | + | |
| 580 | + | |
| 581 | + | |
| 582 | + | |
| 583 | + | |
| 584 | + | |
| 585 | + | |
| 586 | + | |
| 587 | + | |
| 588 | + | |
| 589 | + | |
| 590 | + | |
| 591 | + | |
| 592 | + | |
| 593 | + | |
| 594 | + | |
| 595 | + | |
| 596 | + | |
| 597 | + | |
| 598 | + | |
| 599 | + | |
| 600 | + | |
| 601 | + | |
| 602 | + | |
| 603 | + | |
| 604 | + | |
| 605 | + | |
| 606 | + | |
| 607 | + | |
| 608 | + | |
| 609 | + | |
578 | 610 | | |
579 | 611 | | |
580 | 612 | | |
| |||
0 commit comments