Coverage Report

Created: 2026-08-24 09:36

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
contrib/faiss/faiss/invlists/PreadInvertedLists.cpp
Line
Count
Source
1
/*
2
 * Copyright (c) Meta Platforms, Inc. and affiliates.
3
 *
4
 * This source code is licensed under the MIT license found in the
5
 * LICENSE file in the root directory of this source tree.
6
 */
7
8
#include <faiss/invlists/PreadInvertedLists.h>
9
10
#include <faiss/impl/FaissAssert.h>
11
12
namespace faiss {
13
14
/*******************************************************
15
 * PreadInvertedListsIterator
16
 *******************************************************/
17
18
namespace {
19
20
struct PreadInvertedListsIterator : InvertedListsIterator {
21
    // --- list-level invariants (set once in ctor) ---
22
    size_t list_size = 0;
23
    size_t code_size = 0;
24
25
    // --- borrowed data for the entire list ---
26
    std::unique_ptr<ReadRef> codes_ref_;
27
    std::unique_ptr<ReadRef> ids_ref_;
28
29
    // --- iteration state ---
30
    size_t i = 0;
31
32
    PreadInvertedListsIterator(
33
            const PreadInvertedLists* parent,
34
56
            size_t list_no) {
35
56
        const OnDiskOneList& l = parent->lists.at(list_no);
36
56
        if (l.size == 0) {
37
0
            return;
38
0
        }
39
56
        list_size = l.size;
40
56
        code_size = parent->code_size;
41
56
        const auto* reader = &parent->reader();
42
43
56
        const size_t codes_offset = l.offset;
44
56
        const size_t ids_offset = l.offset + l.capacity * code_size;
45
46
        // Borrow the entire list's codes and ids in one shot.
47
        // A cache-backed reader can return pinned references here,
48
        // giving zero-copy access on repeated queries.
49
56
        codes_ref_ = reader->borrow(codes_offset, list_size * code_size);
50
56
        ids_ref_ = reader->borrow(ids_offset, list_size * sizeof(idx_t));
51
56
    }
52
53
    // non-copyable
54
    PreadInvertedListsIterator(const PreadInvertedListsIterator&) = delete;
55
    PreadInvertedListsIterator& operator=(const PreadInvertedListsIterator&) =
56
            delete;
57
58
1.85k
    bool is_available() const override {
59
1.85k
        return i < list_size;
60
1.85k
    }
61
62
1.78k
    void next() override {
63
1.78k
        ++i;
64
1.78k
    }
65
66
1.80k
    std::pair<idx_t, const uint8_t*> get_id_and_codes() override {
67
1.80k
        auto* ids = reinterpret_cast<const idx_t*>(ids_ref_->data());
68
1.80k
        return {ids[i], codes_ref_->data() + i * code_size};
69
1.80k
    }
70
};
71
72
} // namespace
73
74
/*******************************************************
75
 * PreadInvertedLists
76
 *******************************************************/
77
78
PreadInvertedLists::PreadInvertedLists(const OnDiskInvertedLists& src)
79
8
        : ReadOnlyInvertedLists(src.nlist, src.code_size), lists(src.lists) {
80
8
    use_iterator = true;
81
8
}
82
83
PreadInvertedLists::PreadInvertedLists(
84
        size_t nlist,
85
        size_t code_size,
86
        std::vector<OnDiskOneList> lists)
87
0
        : ReadOnlyInvertedLists(nlist, code_size),
88
0
          lists(std::move(lists)) {
89
0
    use_iterator = true;
90
0
}
91
92
void PreadInvertedLists::set_reader(
93
8
        std::unique_ptr<RandomAccessReader> reader) {
94
8
    reader_ = std::move(reader);
95
8
}
96
97
56
const RandomAccessReader& PreadInvertedLists::reader() const {
98
56
    FAISS_THROW_IF_NOT_MSG(
99
56
            reader_,
100
56
            "PreadInvertedLists: reader not set, call set_reader() first");
101
56
    return *reader_;
102
56
}
103
104
0
size_t PreadInvertedLists::list_size(size_t list_no) const {
105
0
    return lists.at(list_no).size;
106
0
}
107
108
0
const uint8_t* PreadInvertedLists::get_codes(size_t list_no) const {
109
0
    const OnDiskOneList& l = lists.at(list_no);
110
0
    if (l.size == 0) {
111
0
        return nullptr;
112
0
    }
113
0
    auto* out = new uint8_t[l.size * code_size];
114
0
    FAISS_THROW_IF_NOT_MSG(
115
0
            reader_,
116
0
            "PreadInvertedLists: reader not set, call set_reader() first");
117
0
    reader_->read_at(l.offset, out, l.size * code_size);
118
0
    return out;
119
0
}
120
121
0
const idx_t* PreadInvertedLists::get_ids(size_t list_no) const {
122
0
    const OnDiskOneList& l = lists.at(list_no);
123
0
    if (l.size == 0) {
124
0
        return nullptr;
125
0
    }
126
0
    auto* out = new idx_t[l.size];
127
0
    FAISS_THROW_IF_NOT_MSG(
128
0
            reader_,
129
0
            "PreadInvertedLists: reader not set, call set_reader() first");
130
0
    reader_->read_at(
131
0
            l.offset + l.capacity * code_size, out, l.size * sizeof(idx_t));
132
0
    return out;
133
0
}
134
135
void PreadInvertedLists::release_codes(
136
        size_t /*list_no*/,
137
0
        const uint8_t* codes) const {
138
0
    delete[] codes;
139
0
}
140
141
void PreadInvertedLists::release_ids(
142
        size_t /*list_no*/,
143
0
        const idx_t* ids) const {
144
0
    delete[] ids;
145
0
}
146
147
56
bool PreadInvertedLists::is_empty(size_t list_no, void*) const {
148
56
    return lists.at(list_no).size == 0;
149
56
}
150
151
InvertedListsIterator* PreadInvertedLists::get_iterator(
152
        size_t list_no,
153
56
        void*) const {
154
56
    return new PreadInvertedListsIterator(this, list_no);
155
56
}
156
157
void PreadInvertedLists::prefetch_lists(
158
        const idx_t* /*list_nos*/,
159
15
        int /*nlist*/) const {
160
    // Intentionally empty.  The iterator constructor already borrows
161
    // the entire list in one shot, so a synchronous prefetch here
162
    // would just duplicate the same I/O.  If an asynchronous prefetch
163
    // mechanism becomes available in the future, this is the place to
164
    // add it.
165
15
}
166
167
/*******************************************************
168
 * Helper: replace OnDiskInvertedLists with PreadInvertedLists
169
 *******************************************************/
170
171
8
PreadInvertedLists* replace_with_pread_invlists(Index* index) {
172
8
    auto* ivf = dynamic_cast<IndexIVF*>(index);
173
8
    FAISS_THROW_IF_NOT_MSG(
174
8
            ivf, "replace_with_pread_invlists expects an IndexIVF");
175
176
8
    auto* od = dynamic_cast<OnDiskInvertedLists*>(ivf->invlists);
177
8
    FAISS_THROW_IF_NOT_MSG(
178
8
            od,
179
8
            "replace_with_pread_invlists expects IndexIVF with "
180
8
            "OnDiskInvertedLists");
181
182
8
    auto* pread = new PreadInvertedLists(*od);
183
8
    ivf->replace_invlists(pread, true);
184
8
    return pread;
185
8
}
186
187
} // namespace faiss