10 #include "L2Select.cuh"
11 #include "../../FaissAssert.h"
13 #include "../utils/DeviceUtils.h"
14 #include "../utils/MathOperators.cuh"
15 #include "../utils/Pair.cuh"
16 #include "../utils/Reductions.cuh"
17 #include "../utils/Select.cuh"
18 #include "../utils/Tensor.cuh"
19 #include "../utils/StaticUtils.h"
21 namespace faiss {
namespace gpu {
24 template <
typename T,
int kRowsPerBlock,
int kBlockSize>
25 __global__
void l2SelectMin1(Tensor<T, 2, true> productDistances,
26 Tensor<T, 1, true> centroidDistances,
27 Tensor<T, 2, true> outDistances,
28 Tensor<int, 2, true> outIndices) {
30 Pair<T, int> threadMin[kRowsPerBlock];
31 __shared__ Pair<T, int> blockMin[kRowsPerBlock * (kBlockSize / kWarpSize)];
33 T distance[kRowsPerBlock];
36 for (
int i = 0; i < kRowsPerBlock; ++i) {
37 threadMin[i].k = Limits<T>::getMax();
42 int rowStart = blockIdx.x * kRowsPerBlock;
45 bool endRow = (blockIdx.x == gridDim.x - 1);
48 if (productDistances.getSize(0) % kRowsPerBlock == 0) {
54 for (
int row = rowStart; row < productDistances.getSize(0); ++row) {
55 for (
int col = threadIdx.x; col < productDistances.getSize(1);
57 distance[0] = Math<T>::add(centroidDistances[col],
58 productDistances[row][col]);
60 if (Math<T>::lt(distance[0], threadMin[0].k)) {
61 threadMin[0].k = distance[0];
68 blockReduceAll<Pair<T, int>, Min<Pair<T, int> >,
false,
false>(
69 threadMin[0], Min<Pair<T, int> >(), blockMin);
71 if (threadIdx.x == 0) {
72 outDistances[row][0] = threadMin[0].k;
73 outIndices[row][0] = threadMin[0].v;
79 threadMin[0].k = Limits<T>::getMax();
83 for (
int col = threadIdx.x; col < productDistances.getSize(1);
85 T centroidDistance = centroidDistances[col];
88 for (
int row = 0; row < kRowsPerBlock; ++row) {
89 distance[row] = productDistances[rowStart + row][col];
93 for (
int row = 0; row < kRowsPerBlock; ++row) {
94 distance[row] = Math<T>::add(distance[row], centroidDistance);
98 for (
int row = 0; row < kRowsPerBlock; ++row) {
99 if (Math<T>::lt(distance[row], threadMin[row].k)) {
100 threadMin[row].k = distance[row];
101 threadMin[row].v = col;
107 blockReduceAll<kRowsPerBlock, Pair<T, int>, Min<Pair<T, int> >,
false,
false>(
108 threadMin, Min<Pair<T, int> >(), blockMin);
110 if (threadIdx.x == 0) {
112 for (
int row = 0; row < kRowsPerBlock; ++row) {
113 outDistances[rowStart + row][0] = threadMin[row].k;
114 outIndices[rowStart + row][0] = threadMin[row].v;
121 template <
typename T,
int NumWarpQ,
int NumThreadQ,
int ThreadsPerBlock>
122 __global__
void l2SelectMinK(Tensor<T, 2, true> productDistances,
123 Tensor<T, 1, true> centroidDistances,
124 Tensor<T, 2, true> outDistances,
125 Tensor<int, 2, true> outIndices,
128 constexpr
int kNumWarps = ThreadsPerBlock / kWarpSize;
130 __shared__ T smemK[kNumWarps * NumWarpQ];
131 __shared__
int smemV[kNumWarps * NumWarpQ];
133 BlockSelect<T, int, false, Comparator<T>,
134 NumWarpQ, NumThreadQ, ThreadsPerBlock>
135 heap(initK, -1, smemK, smemV, k);
137 int row = blockIdx.x;
140 int limit = utils::roundDown(productDistances.getSize(1), kWarpSize);
143 for (; i < limit; i += blockDim.x) {
144 T v = Math<T>::add(centroidDistances[i],
145 productDistances[row][i]);
149 if (i < productDistances.getSize(1)) {
150 T v = Math<T>::add(centroidDistances[i],
151 productDistances[row][i]);
152 heap.addThreadQ(v, i);
156 for (
int i = threadIdx.x; i < k; i += blockDim.x) {
157 outDistances[row][i] = smemK[i];
158 outIndices[row][i] = smemV[i];
163 template <
typename T>
164 void runL2SelectMin(Tensor<T, 2, true>& productDistances,
165 Tensor<T, 1, true>& centroidDistances,
166 Tensor<T, 2, true>& outDistances,
167 Tensor<int, 2, true>& outIndices,
169 cudaStream_t stream) {
170 FAISS_ASSERT(productDistances.getSize(0) == outDistances.getSize(0));
171 FAISS_ASSERT(productDistances.getSize(0) == outIndices.getSize(0));
172 FAISS_ASSERT(centroidDistances.getSize(0) == productDistances.getSize(1));
173 FAISS_ASSERT(outDistances.getSize(1) == k);
174 FAISS_ASSERT(outIndices.getSize(1) == k);
175 FAISS_ASSERT(k <= 1024);
178 constexpr
int kThreadsPerBlock = 256;
179 constexpr
int kRowsPerBlock = 8;
181 auto block = dim3(kThreadsPerBlock);
182 auto grid = dim3(utils::divUp(outDistances.getSize(0), kRowsPerBlock));
184 l2SelectMin1<T, kRowsPerBlock, kThreadsPerBlock>
185 <<<grid, block, 0, stream>>>(productDistances, centroidDistances,
186 outDistances, outIndices);
188 constexpr
int kThreadsPerBlock = 128;
190 auto block = dim3(kThreadsPerBlock);
191 auto grid = dim3(outDistances.getSize(0));
193 #define RUN_L2_SELECT(NUM_WARP_Q, NUM_THREAD_Q) \
195 l2SelectMinK<T, NUM_WARP_Q, NUM_THREAD_Q, kThreadsPerBlock> \
196 <<<grid, block, 0, stream>>>(productDistances, centroidDistances, \
197 outDistances, outIndices, \
198 k, Limits<T>::getMax()); \
202 RUN_L2_SELECT(32, 2);
203 }
else if (k <= 64) {
204 RUN_L2_SELECT(64, 3);
205 }
else if (k <= 128) {
206 RUN_L2_SELECT(128, 3);
207 }
else if (k <= 256) {
208 RUN_L2_SELECT(256, 4);
209 }
else if (k <= 512) {
210 RUN_L2_SELECT(512, 8);
211 }
else if (k <= 1024) {
212 RUN_L2_SELECT(1024, 8);
221 void runL2SelectMin(Tensor<float, 2, true>& productDistances,
222 Tensor<float, 1, true>& centroidDistances,
223 Tensor<float, 2, true>& outDistances,
224 Tensor<int, 2, true>& outIndices,
226 cudaStream_t stream) {
227 runL2SelectMin<float>(productDistances,
235 #ifdef FAISS_USE_FLOAT16
236 void runL2SelectMin(Tensor<half, 2, true>& productDistances,
237 Tensor<half, 1, true>& centroidDistances,
238 Tensor<half, 2, true>& outDistances,
239 Tensor<int, 2, true>& outIndices,
241 cudaStream_t stream) {
242 runL2SelectMin<half>(productDistances,