mirror of
https://github.com/facebookresearch/faiss.git
synced 2026-10-12 02:13:36 +00:00
Update default Panorama batch size to 1024 (#5441)
Summary: ## Changes This PR unifies the default Panorama batch size to 1024 going forward. This was shown to offer better performance than the current values ([paper](https://arxiv.org/abs/2510.00566), figure 11). ## Performance ### IVFFlatPanorama SIFT1M | nprobe | qps_before | qps_after | speedup | |---|---|---|---| | 1 | 5980.4 | 6343.7 | 1.06x | | 2 | 3537.7 | 3933.2 | 1.11x | | 4 | 2105.4 | 2419.9 | 1.15x | | 8 | 1245.2 | 1466.4 | 1.18x | | 16 | 729.2 | 887.4 | 1.22x | | 32 | 429.6 | 538.0 | 1.25x | | 64 | 249.0 | 319.3 | 1.28x | GIST1M | nprobe | qps_before | qps_after | speedup | |---|---|---|---| | 1 | 1826.0 | 1494.7 | 0.82x | | 2 | 1091.0 | 1001.7 | 0.92x | | 4 | 642.2 | 639.0 | 0.99x | | 8 | 368.7 | 392.7 | 1.07x | | 16 | 212.2 | 233.9 | 1.10x | | 32 | 122.2 | 138.6 | 1.13x | | 64 | 71.3 | 82.6 | 1.16x | ## FlatL2Panorama SIFT1M | qps_before | qps_after | speedup | |---|---|---| | 124.8 | 133.4 | 1.07x | GIST1M | qps_before | qps_after | speedup | |---|---|---| | 44.1 | 46.2 | 1.05x | Pull Request resolved: https://github.com/facebookresearch/faiss/pull/5441 Reviewed By: alibeklfc Differential Revision: D114387063 Pulled By: mnorris11 fbshipit-source-id: b71ba6ced074c4998aee3183df236b692626cd34
This commit is contained in:
committed by
meta-codesync[bot]
parent
3b50babc59
commit
76c67b7b73
@@ -69,13 +69,12 @@ def build_index(name):
|
||||
|
||||
|
||||
nlevels = 16 if args.dataset == "gist1m" else 8
|
||||
batch_size = 512
|
||||
|
||||
plt.figure(figsize=(8, 6), dpi=80)
|
||||
|
||||
names = [
|
||||
"Flat",
|
||||
f"PCA{d},FlatL2Panorama{nlevels}_{batch_size}",
|
||||
f"PCA{d},FlatL2Panorama{nlevels}",
|
||||
]
|
||||
|
||||
labels = []
|
||||
|
||||
@@ -98,7 +98,7 @@ plt.figure(figsize=(8, 6), dpi=80)
|
||||
eval_and_plot(f"IVF{nlist},Flat")
|
||||
|
||||
# IVFFlatPanorama (with PCA transform to concentrate energy in early dimensions)
|
||||
eval_and_plot(f"PCA{d},IVF{nlist},FlatPanorama{nlevels}_{1024}")
|
||||
eval_and_plot(f"PCA{d},IVF{nlist},FlatPanorama{nlevels}")
|
||||
|
||||
dataset_label = args.dataset.upper()
|
||||
plt.title(f"IVF Flat Indexes on {dataset_label}")
|
||||
|
||||
+2
-2
@@ -176,7 +176,7 @@ struct IndexFlatL2Panorama : IndexFlatPanorama {
|
||||
explicit IndexFlatL2Panorama(
|
||||
idx_t d_in,
|
||||
size_t n_levels_in,
|
||||
size_t batch_size_in = 512)
|
||||
size_t batch_size_in = Panorama::kDefaultBatchSize)
|
||||
: IndexFlatPanorama(d_in, METRIC_L2, n_levels_in, batch_size_in) {}
|
||||
};
|
||||
|
||||
@@ -189,7 +189,7 @@ struct IndexFlatIPPanorama : IndexFlatPanorama {
|
||||
explicit IndexFlatIPPanorama(
|
||||
idx_t d_in,
|
||||
size_t n_levels_in,
|
||||
size_t batch_size_in = 512)
|
||||
size_t batch_size_in = Panorama::kDefaultBatchSize)
|
||||
: IndexFlatPanorama(
|
||||
d_in,
|
||||
METRIC_INNER_PRODUCT,
|
||||
|
||||
@@ -235,7 +235,8 @@ inline auto with_bool(bool value, Lambda&& fn) {
|
||||
* accelerating the refinement stage.
|
||||
*/
|
||||
struct Panorama {
|
||||
static constexpr size_t kDefaultBatchSize = 128;
|
||||
static constexpr size_t kDefaultBatchSize = 1024;
|
||||
static constexpr size_t kLegacyBatchSize = 128;
|
||||
|
||||
size_t d = 0;
|
||||
size_t code_size = 0;
|
||||
|
||||
@@ -545,7 +545,7 @@ std::unique_ptr<InvertedLists> read_InvertedLists_up(
|
||||
READ1(n_levels);
|
||||
FAISS_THROW_IF_NOT_FMT(
|
||||
n_levels > 0, "invalid ilpn n_levels %zd", n_levels);
|
||||
constexpr size_t bs = Panorama::kDefaultBatchSize;
|
||||
constexpr size_t bs = Panorama::kLegacyBatchSize;
|
||||
auto ailp = std::make_unique<ArrayInvertedListsPanorama>(
|
||||
nlist, code_size, n_levels, bs);
|
||||
std::vector<size_t> sizes(nlist);
|
||||
@@ -2136,7 +2136,7 @@ std::unique_ptr<Index> read_index_up(IOReader* f, int io_flags) {
|
||||
read_ivf_header(ivfp.get(), f);
|
||||
ivfp->code_size = ivfp->d * sizeof(float);
|
||||
READ1(ivfp->n_levels);
|
||||
ivfp->batch_size = Panorama::kDefaultBatchSize;
|
||||
ivfp->batch_size = Panorama::kLegacyBatchSize;
|
||||
read_InvertedLists(*ivfp, f, io_flags);
|
||||
idx = std::move(ivfp);
|
||||
} else if (h == fourcc("IwP2")) {
|
||||
|
||||
+11
-26
@@ -275,20 +275,12 @@ void write_InvertedLists(const InvertedLists* ils, IOWriter* f) {
|
||||
} else if (
|
||||
const auto& ailp =
|
||||
dynamic_cast<const ArrayInvertedListsPanorama*>(ils)) {
|
||||
if (ailp->pano.batch_size == Panorama::kDefaultBatchSize) {
|
||||
uint32_t h = fourcc("ilpn");
|
||||
WRITE1(h);
|
||||
WRITE1(ailp->nlist);
|
||||
WRITE1(ailp->code_size);
|
||||
WRITE1(ailp->n_levels);
|
||||
} else {
|
||||
uint32_t h = fourcc("ilp2");
|
||||
WRITE1(h);
|
||||
WRITE1(ailp->nlist);
|
||||
WRITE1(ailp->code_size);
|
||||
WRITE1(ailp->n_levels);
|
||||
WRITE1(ailp->pano.batch_size);
|
||||
}
|
||||
uint32_t h = fourcc("ilp2");
|
||||
WRITE1(h);
|
||||
WRITE1(ailp->nlist);
|
||||
WRITE1(ailp->code_size);
|
||||
WRITE1(ailp->n_levels);
|
||||
WRITE1(ailp->pano.batch_size);
|
||||
uint32_t list_type = fourcc("full");
|
||||
WRITE1(list_type);
|
||||
std::vector<size_t> sizes;
|
||||
@@ -736,18 +728,11 @@ void write_index(const Index* idx, IOWriter* f, int io_flags) {
|
||||
} else if (
|
||||
const IndexIVFFlatPanorama* ivfp =
|
||||
dynamic_cast<const IndexIVFFlatPanorama*>(idx)) {
|
||||
if (ivfp->batch_size == Panorama::kDefaultBatchSize) {
|
||||
uint32_t h = fourcc("IwPn");
|
||||
WRITE1(h);
|
||||
write_ivf_header(ivfp, f);
|
||||
WRITE1(ivfp->n_levels);
|
||||
} else {
|
||||
uint32_t h = fourcc("IwP2");
|
||||
WRITE1(h);
|
||||
write_ivf_header(ivfp, f);
|
||||
WRITE1(ivfp->n_levels);
|
||||
WRITE1(ivfp->batch_size);
|
||||
}
|
||||
uint32_t h = fourcc("IwP2");
|
||||
WRITE1(h);
|
||||
write_ivf_header(ivfp, f);
|
||||
WRITE1(ivfp->n_levels);
|
||||
WRITE1(ivfp->batch_size);
|
||||
write_InvertedLists(ivfp->invlists, f);
|
||||
} else if (
|
||||
const IndexIVFFlat* ivfl_2 =
|
||||
|
||||
@@ -385,7 +385,7 @@ IndexIVF* parse_IndexIVF(
|
||||
}
|
||||
if (match("FlatPanorama([0-9]+)?(_([0-9]+))?")) {
|
||||
int nlevels = mres_to_int(sm[1], 8); // default to 8 levels
|
||||
int bs = mres_to_int(sm[3], 128);
|
||||
int bs = mres_to_int(sm[3], Panorama::kDefaultBatchSize);
|
||||
return new IndexIVFFlatPanorama(
|
||||
get_q(), d, nlist, nlevels, mt, own_il, bs);
|
||||
}
|
||||
|
||||
@@ -71,6 +71,7 @@ class TestIndexIVFFlatPanorama(unittest.TestCase):
|
||||
nprobe=None,
|
||||
make_direct_map=False,
|
||||
metric=faiss.METRIC_L2,
|
||||
batch_size=faiss.Panorama.kDefaultBatchSize,
|
||||
):
|
||||
"""Create and initialize IndexIVFFlatPanorama."""
|
||||
quantizer = (
|
||||
@@ -78,7 +79,9 @@ class TestIndexIVFFlatPanorama(unittest.TestCase):
|
||||
if metric == faiss.METRIC_L2
|
||||
else faiss.IndexFlatIP(d)
|
||||
)
|
||||
index = faiss.IndexIVFFlatPanorama(quantizer, d, nlist, nlevels, metric)
|
||||
index = faiss.IndexIVFFlatPanorama(
|
||||
quantizer, d, nlist, nlevels, metric, True, batch_size
|
||||
)
|
||||
index.train(xt)
|
||||
if make_direct_map:
|
||||
index.make_direct_map()
|
||||
@@ -769,6 +772,56 @@ class TestIndexIVFFlatPanorama(unittest.TestCase):
|
||||
np.testing.assert_array_equal(I_before, I_after)
|
||||
np.testing.assert_array_equal(D_before, D_after)
|
||||
|
||||
def test_read_legacy_format(self):
|
||||
"""Indexes serialized in the legacy "IwPn"/"ilpn" format (which does
|
||||
not store batch_size and implies the legacy value of 128) must keep
|
||||
deserializing correctly, whatever the current default batch_size is.
|
||||
|
||||
The current code always writes the explicit-batch_size format
|
||||
("IwP2"/"ilp2"), so a legacy stream is reconstructed here by
|
||||
transforming a serialized index: swap the fourccs and drop the two
|
||||
8-byte batch_size fields.
|
||||
"""
|
||||
d, nlist, nlevels, nb, nq, k = 32, 4, 8, 2000, 10, 10
|
||||
legacy_bs = 128
|
||||
rng = np.random.RandomState(123)
|
||||
xb = rng.rand(nb, d).astype("float32")
|
||||
xq = rng.rand(nq, d).astype("float32")
|
||||
|
||||
quantizer = faiss.IndexFlatL2(d)
|
||||
index = faiss.IndexIVFFlatPanorama(
|
||||
quantizer, d, nlist, nlevels, faiss.METRIC_L2, True, legacy_bs
|
||||
)
|
||||
index.train(xb)
|
||||
index.add(xb)
|
||||
index.nprobe = nlist
|
||||
D_ref, I_ref = index.search(xq, k)
|
||||
|
||||
buf = faiss.serialize_index(index).tobytes()
|
||||
# layout: "IwP2" | ivf header | n_levels (8) | batch_size (8) |
|
||||
# "ilp2" | nlist (8) | code_size (8) | n_levels (8) |
|
||||
# batch_size (8) | inverted lists data
|
||||
self.assertEqual(buf[:4], b"IwP2")
|
||||
self.assertEqual(buf.count(b"ilp2"), 1)
|
||||
p = buf.index(b"ilp2")
|
||||
legacy = (
|
||||
b"IwPn"
|
||||
+ buf[4 : p - 8] # drop the IVF-level batch_size field
|
||||
+ b"ilpn"
|
||||
+ buf[p + 4 : p + 28] # nlist, code_size, n_levels
|
||||
+ buf[p + 36 :] # drop the invlist-level batch_size field
|
||||
)
|
||||
|
||||
index_legacy = faiss.deserialize_index(
|
||||
np.frombuffer(legacy, dtype=np.uint8)
|
||||
)
|
||||
self.assertIsInstance(index_legacy, faiss.IndexIVFFlatPanorama)
|
||||
self.assertEqual(index_legacy.batch_size, legacy_bs)
|
||||
index_legacy.nprobe = nlist
|
||||
D_legacy, I_legacy = index_legacy.search(xq, k)
|
||||
np.testing.assert_array_equal(I_ref, I_legacy)
|
||||
np.testing.assert_array_equal(D_ref, D_legacy)
|
||||
|
||||
def test_ratio_dims_scanned(self):
|
||||
"""Test the correctness of the ratio of dimensions scanned"""
|
||||
d, nb, nq, nlist, k = 128, 500000, 1, 1, 1
|
||||
@@ -798,7 +851,14 @@ class TestIndexIVFFlatPanorama(unittest.TestCase):
|
||||
with self.subTest(nlevels=nlevels):
|
||||
faiss.cvar.indexPanorama_stats.reset()
|
||||
index = self.create_panorama(
|
||||
d, nlist, nlevels, xt, xb, nprobe=1, metric=metric
|
||||
d,
|
||||
nlist,
|
||||
nlevels,
|
||||
xt,
|
||||
xb,
|
||||
nprobe=1,
|
||||
metric=metric,
|
||||
batch_size=128,
|
||||
)
|
||||
D, I = index.search(xq, k)
|
||||
self.assert_search_results_equal(D_base, I_base, D, I)
|
||||
|
||||
Reference in New Issue
Block a user