faiss/gpu/GpuIndexIVFFlat.cu

244 lines
6.7 KiB
Plaintext
Raw Normal View History

2017-02-23 06:26:44 +08:00
/**
* Copyright (c) Facebook, Inc. and its affiliates.
2017-02-23 06:26:44 +08:00
*
* This source code is licensed under the MIT license found in the
2017-02-23 06:26:44 +08:00
* LICENSE file in the root directory of this source tree.
*/
#include <faiss/gpu/GpuIndexIVFFlat.h>
#include <faiss/IndexFlat.h>
#include <faiss/IndexIVFFlat.h>
#include <faiss/gpu/GpuIndexFlat.h>
#include <faiss/gpu/GpuResources.h>
#include <faiss/gpu/impl/IVFFlat.cuh>
#include <faiss/gpu/utils/CopyUtils.cuh>
#include <faiss/gpu/utils/DeviceUtils.h>
#include <faiss/gpu/utils/Float16.cuh>
2017-02-23 06:26:44 +08:00
#include <limits>
namespace faiss { namespace gpu {
GpuIndexIVFFlat::GpuIndexIVFFlat(GpuResources* resources,
const faiss::IndexIVFFlat* index,
GpuIndexIVFFlatConfig config) :
2017-02-23 06:26:44 +08:00
GpuIndexIVF(resources,
index->d,
index->metric_type,
2020-03-10 21:24:07 +08:00
index->metric_arg,
index->nlist,
config),
ivfFlatConfig_(config),
2017-02-23 06:26:44 +08:00
reserveMemoryVecs_(0),
index_(nullptr) {
copyFrom(index);
2017-02-23 06:26:44 +08:00
}
GpuIndexIVFFlat::GpuIndexIVFFlat(GpuResources* resources,
int dims,
int nlist,
faiss::MetricType metric,
GpuIndexIVFFlatConfig config) :
2020-03-10 21:24:07 +08:00
GpuIndexIVF(resources, dims, metric, 0, nlist, config),
ivfFlatConfig_(config),
2017-02-23 06:26:44 +08:00
reserveMemoryVecs_(0),
index_(nullptr) {
// faiss::Index params
this->is_trained = false;
2017-02-23 06:26:44 +08:00
// We haven't trained ourselves, so don't construct the IVFFlat
// index yet
2017-02-23 06:26:44 +08:00
}
GpuIndexIVFFlat::~GpuIndexIVFFlat() {
delete index_;
}
void
GpuIndexIVFFlat::reserveMemory(size_t numVecs) {
reserveMemoryVecs_ = numVecs;
if (index_) {
2020-03-10 21:24:07 +08:00
DeviceScope scope(device_);
2017-02-23 06:26:44 +08:00
index_->reserveMemory(numVecs);
}
}
void
GpuIndexIVFFlat::copyFrom(const faiss::IndexIVFFlat* index) {
DeviceScope scope(device_);
GpuIndexIVF::copyFrom(index);
// Clear out our old data
delete index_;
index_ = nullptr;
// The other index might not be trained
if (!index->is_trained) {
2020-03-10 21:24:07 +08:00
FAISS_ASSERT(!is_trained);
2017-02-23 06:26:44 +08:00
return;
}
// Otherwise, we can populate ourselves from the other index
2020-03-10 21:24:07 +08:00
FAISS_ASSERT(is_trained);
2017-02-23 06:26:44 +08:00
// Copy our lists as well
index_ = new IVFFlat(resources_,
quantizer->getGpuData(),
index->metric_type,
2020-03-10 21:24:07 +08:00
index->metric_arg,
false, // no residual
nullptr, // no scalar quantizer
ivfFlatConfig_.indicesOptions,
memorySpace_);
InvertedLists *ivf = index->invlists;
2017-02-23 06:26:44 +08:00
for (size_t i = 0; i < ivf->nlist; ++i) {
auto numVecs = ivf->list_size(i);
2017-02-23 06:26:44 +08:00
// GPU index can only support max int entries per list
FAISS_THROW_IF_NOT_FMT(numVecs <=
(size_t) std::numeric_limits<int>::max(),
"GPU inverted list can only support "
"%zu entries; %zu found",
(size_t) std::numeric_limits<int>::max(),
numVecs);
index_->addCodeVectorsFromCpu(i,
(const unsigned char*)(ivf->get_codes(i)),
ivf->get_ids(i),
numVecs);
2017-02-23 06:26:44 +08:00
}
}
void
GpuIndexIVFFlat::copyTo(faiss::IndexIVFFlat* index) const {
DeviceScope scope(device_);
// We must have the indices in order to copy to ourselves
FAISS_THROW_IF_NOT_MSG(ivfFlatConfig_.indicesOptions != INDICES_IVF,
"Cannot copy to CPU as GPU index doesn't retain "
"indices (INDICES_IVF)");
2017-02-23 06:26:44 +08:00
GpuIndexIVF::copyTo(index);
index->code_size = this->d * sizeof(float);
InvertedLists *ivf = new ArrayInvertedLists(nlist, index->code_size);
index->replace_invlists(ivf, true);
2017-02-23 06:26:44 +08:00
// Copy the inverted lists
if (index_) {
for (int i = 0; i < nlist; ++i) {
auto listIndices = index_->getListIndices(i);
auto listData = index_->getListVectors(i);
ivf->add_entries(i,
listIndices.size(),
listIndices.data(),
(const uint8_t*) listData.data());
2017-02-23 06:26:44 +08:00
}
}
}
size_t
GpuIndexIVFFlat::reclaimMemory() {
if (index_) {
DeviceScope scope(device_);
return index_->reclaimMemory();
}
return 0;
}
void
GpuIndexIVFFlat::reset() {
if (index_) {
DeviceScope scope(device_);
index_->reset();
this->ntotal = 0;
} else {
FAISS_ASSERT(this->ntotal == 0);
}
}
void
GpuIndexIVFFlat::train(Index::idx_t n, const float* x) {
DeviceScope scope(device_);
if (this->is_trained) {
FAISS_ASSERT(quantizer->is_trained);
FAISS_ASSERT(quantizer->ntotal == nlist);
2017-02-23 06:26:44 +08:00
FAISS_ASSERT(index_);
return;
}
FAISS_ASSERT(!index_);
trainQuantizer_(n, x);
// The quantizer is now trained; construct the IVF index
index_ = new IVFFlat(resources_,
quantizer->getGpuData(),
this->metric_type,
2020-03-10 21:24:07 +08:00
this->metric_arg,
false, // no residual
nullptr, // no scalar quantizer
ivfFlatConfig_.indicesOptions,
memorySpace_);
2017-02-23 06:26:44 +08:00
if (reserveMemoryVecs_) {
index_->reserveMemory(reserveMemoryVecs_);
}
this->is_trained = true;
}
void
GpuIndexIVFFlat::addImpl_(int n,
const float* x,
const Index::idx_t* xids) {
// Device is already set in GpuIndex::add
2017-02-23 06:26:44 +08:00
FAISS_ASSERT(index_);
FAISS_ASSERT(n > 0);
2017-02-23 06:26:44 +08:00
// Data is already resident on the GPU
Tensor<float, 2, true> data(const_cast<float*>(x), {n, (int) this->d});
2017-02-23 06:26:44 +08:00
static_assert(sizeof(long) == sizeof(Index::idx_t), "size mismatch");
Tensor<long, 1, true> labels(const_cast<long*>(xids), {n});
// Not all vectors may be able to be added (some may contain NaNs etc)
index_->classifyAndAddVectors(data, labels);
// but keep the ntotal based on the total number of vectors that we attempted
// to add
ntotal += n;
2017-02-23 06:26:44 +08:00
}
void
GpuIndexIVFFlat::searchImpl_(int n,
const float* x,
int k,
float* distances,
Index::idx_t* labels) const {
// Device is already set in GpuIndex::search
2017-02-23 06:26:44 +08:00
FAISS_ASSERT(index_);
FAISS_ASSERT(n > 0);
2017-02-23 06:26:44 +08:00
// Data is already resident on the GPU
Tensor<float, 2, true> queries(const_cast<float*>(x), {n, (int) this->d});
Tensor<float, 2, true> outDistances(distances, {n, k});
static_assert(sizeof(long) == sizeof(Index::idx_t), "size mismatch");
Tensor<long, 2, true> outLabels(const_cast<long*>(labels), {n, k});
index_->query(queries, nprobe, k, outDistances, outLabels);
2017-02-23 06:26:44 +08:00
}
} } // namespace