* -------------------------------------------------------------------------
* This file is part of the IndexSDK project.
* Copyright (c) 2026 Huawei Technologies Co.,Ltd.
*
* IndexSDK is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*
* http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
* -------------------------------------------------------------------------
*/
#include <faiss/ascend/AscendIndexCagra.h>
#include <gtest/gtest.h>
#include <sys/time.h>
#include <algorithm>
#include <cfloat>
#include <cmath>
#include <cstdlib>
#include <iostream>
#include <numeric>
#include <random>
#include <vector>
namespace
{
unsigned int g_seed;
const int FAST_RAND_MAX = 0x7FFF;
const int MILLI_SECOND = 1000;
inline double GetMillisecs()
{
struct timeval tv = {0, 0};
gettimeofday(&tv, nullptr);
return tv.tv_sec * 1e3 + tv.tv_usec * 1e-3;
}
inline int FastRand()
{
const int multiplyNum = 214013;
const int addNum = 2531011;
const int rshiftNum = 16;
g_seed = (multiplyNum * g_seed + addNum);
return (g_seed >> rshiftNum) & FAST_RAND_MAX;
}
void Norm(float *data, size_t n, int dim)
{
#pragma omp parallel for if (n > 100)
for (size_t i = 0; i < n; ++i)
{
float l2norm = 0;
for (int j = 0; j < dim; ++j)
{
l2norm += data[i * dim + j] * data[i * dim + j];
}
l2norm = std::sqrt(l2norm);
if (fabs(l2norm) < FLT_EPSILON)
{
std::cerr << "Error: Invalid l2norm value." << std::endl;
continue;
}
for (int j = 0; j < dim; ++j)
{
data[i * dim + j] = data[i * dim + j] / l2norm;
}
}
}
TEST(TestAscendIndexCagra, QPS)
{
int dim = 128;
int ntotal = 10000;
int graphDegree = 64;
int topK = 32;
try
{
std::vector<int> deviceList = {0};
faiss::ascend::AscendIndexCagra index;
auto ret = index.Init(dim, graphDegree, ntotal, topK, deviceList);
ASSERT_EQ(ret, 0);
std::mt19937 rng(42);
std::uniform_real_distribution<float> distFloat(0.0f, 1.0f);
std::uniform_int_distribution<uint32_t> distUint(0, static_cast<uint32_t>(ntotal - 1));
std::vector<float> baseData(ntotal * dim);
for (auto &v : baseData)
{
v = distFloat(rng);
}
Norm(baseData.data(), ntotal, dim);
std::vector<uint32_t> graphData(ntotal * graphDegree);
for (auto &v : graphData)
{
v = distUint(rng);
}
std::vector<uint32_t> hashData(ntotal * 2);
for (auto &v : hashData)
{
v = distUint(rng);
}
ret = index.Add(graphData.data(), hashData.data(), baseData.data());
EXPECT_EQ(ret, 0);
printf("add done\n");
int queryNum = 64;
std::vector<float> queryData(queryNum * dim);
for (auto &v : queryData)
{
v = distFloat(rng);
}
Norm(queryData.data(), queryNum, dim);
ret = index.QuantizeData(queryNum, queryData.data(), ntotal, baseData.data());
EXPECT_EQ(ret, 0);
printf("quantize done\n");
std::vector<float> distw(queryNum * topK, 0);
std::vector<uint32_t> labelw(queryNum * topK, 0);
for (int i = 0; i < 5; i++)
{
index.Search(queryNum, queryData.data(), topK, distw.data(), labelw.data());
}
int loopTimes = 100;
std::vector<float> dist(queryNum * topK, 0);
std::vector<uint32_t> label(queryNum * topK, 0);
double ts = GetMillisecs();
for (int i = 0; i < loopTimes; i++)
{
index.Search(queryNum, queryData.data(), topK, dist.data(), label.data());
}
double te = GetMillisecs();
printf("CAGRA QPS test: base=%d, dim=%d, graphDegree=%d, topK=%d, queryNum=%d, QPS=%.4f\n", ntotal, dim,
graphDegree, topK, queryNum, MILLI_SECOND * queryNum * loopTimes / (te - ts));
}
catch (std::exception &e)
{
FAIL() << "Exception occurred: " << e.what();
}
}
}
int main(int argc, char **argv)
{
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}