Skip to content

Commit a7472dc

Browse files
committed
ITS GPU Seeding vertexer: use warp reductions for fit peak kernel
1 parent fe8ac9b commit a7472dc

4 files changed

Lines changed: 261 additions & 96 deletions

File tree

‎Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu‎

Lines changed: 85 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
#include "ITStrackingGPU/TrackingKernels.h"
5353
#include "ITStrackingGPU/Utils.h"
5454
#include "MathUtils/Utils.h"
55+
#include "GPUCommonAlgorithm.h"
5556
#include "ITStracking/ClusterLines.h"
5657
#include "ITStracking/LineProjection.h"
5758
#include "utils/strtag.h"
@@ -1000,85 +1001,108 @@ GPUg() void fitPeaksKernel(const int* nPeaksDevice,
10001001
VertexCand* cands)
10011002
{
10021003
const int nPeaks = *nPeaksDevice;
1003-
const o2::its::TimeEstBC* t = lineProjSorted.t;
10041004
const int* idx = lineProjSorted.idx;
1005-
for (int p = blockIdx.x * blockDim.x + threadIdx.x; p < nPeaks; p += blockDim.x * gridDim.x) {
1006-
cands[p].ok = 0;
1007-
cands[p].nGood = 0;
1008-
const int k = peakLineIdx[p];
1009-
cands[p].fine = isZPeakFine != nullptr ? isZPeakFine[k] : 0;
1005+
const o2::its::TimeEstBC* t = lineProjSorted.t;
1006+
const int lane = threadIdx.x % gpu::WarpSize;
1007+
const int nWarps = blockDim.x * gridDim.x / gpu::WarpSize;
1008+
const int iWarp = (blockIdx.x * blockDim.x + threadIdx.x) / gpu::WarpSize;
1009+
for (int iPeak = iWarp; iPeak < nPeaks; iPeak += nWarps) {
1010+
const int k = peakLineIdx[iPeak];
1011+
if (lane == 0) {
1012+
cands[iPeak].ok = 0;
1013+
cands[iPeak].nGood = 0;
1014+
cands[iPeak].fine = isZPeakFine != nullptr ? isZPeakFine[k] : 0;
1015+
}
10101016
const auto tk = t[k];
10111017
const LineWindow wk = win[k];
1012-
1013-
o2::its::ClusterLines seed;
10141018
int nMembers = 0;
1015-
for (int j = wk.lo; j < wk.hi; ++j) {
1016-
const auto tj = t[j];
1017-
if (tk.isCompatible(tj)) {
1018-
seed.add(lines[idx[j]]);
1019-
++nMembers;
1019+
WarpFit seed;
1020+
for (int iLine = wk.lo + lane; iLine < wk.hi; iLine += gpu::WarpSize) {
1021+
const auto tj = t[iLine];
1022+
if (!tk.isCompatible(tj)) {
1023+
continue;
10201024
}
1025+
seed.add(lines[idx[iLine]]);
1026+
++nMembers;
10211027
}
1022-
float seedVertex[3];
1023-
if (nMembers < 2 || !seed.solve(seedVertex)) {
1028+
seed.reduce<gpu::WarpSize>();
1029+
nMembers = warp_reduce_add<gpu::WarpSize>(nMembers);
1030+
seed.computeClusterCentroid();
1031+
if (nMembers < 2 || !seed.isValid()) {
10241032
continue;
10251033
}
1034+
const float* seedVertex = seed.getVertex();
10261035

1027-
o2::its::ClusterLines fit;
1036+
WarpFit fit;
10281037
int nKept = 0;
10291038
int nGood = 0;
1030-
for (int j = wk.lo; j < wk.hi; ++j) {
1031-
const auto tj = t[j];
1032-
if (tk.isCompatible(tj)) {
1033-
const o2::its::Line& line = lines[idx[j]];
1034-
if (o2::its::Line::getDistance2FromPoint(line, seedVertex) < pairCut2) {
1035-
fit.add(line);
1036-
const float c = lineChi2[idx[j]];
1037-
const float pt = linePt[idx[j]];
1038-
const bool okChi2 = (goodLineChi2Cut <= 0.f || c <= goodLineChi2Cut);
1039-
const bool okPt = (goodLinePtCut <= 0.f || pt >= goodLinePtCut);
1040-
nGood += okChi2 && okPt;
1041-
++nKept;
1042-
}
1039+
for (int iLine = wk.lo + lane; iLine < wk.hi; iLine += gpu::WarpSize) {
1040+
const auto tj = t[iLine];
1041+
if (!tk.isCompatible(tj)) {
1042+
continue;
1043+
}
1044+
const o2::its::Line& line = lines[idx[iLine]];
1045+
if (!(o2::its::Line::getDistance2FromPoint(line, seedVertex) < pairCut2)) {
1046+
continue;
10431047
}
1048+
fit.add(line);
1049+
const float c = lineChi2[idx[iLine]];
1050+
const float pt = linePt[idx[iLine]];
1051+
const bool okChi2 = (goodLineChi2Cut <= 0.f || c <= goodLineChi2Cut);
1052+
const bool okPt = (goodLinePtCut <= 0.f || pt >= goodLinePtCut);
1053+
nGood += okChi2 && okPt;
1054+
++nKept;
10441055
}
1045-
float vertex[3];
1046-
if (nKept < 2 || !fit.solve(vertex)) {
1056+
fit.reduce<gpu::WarpSize>();
1057+
nKept = warp_reduce_add<gpu::WarpSize>(nKept);
1058+
nGood = warp_reduce_add<gpu::WarpSize>(nGood);
1059+
fit.computeClusterCentroid();
1060+
if (nKept < 2 || !fit.isValid()) {
10471061
continue;
10481062
}
1049-
cands[p].seed[0] = seedVertex[0];
1050-
cands[p].seed[1] = seedVertex[1];
1051-
cands[p].seed[2] = seedVertex[2];
1063+
const float* vertex = fit.getVertex();
1064+
1065+
if (lane == 0) {
1066+
cands[iPeak].seed[0] = seedVertex[0];
1067+
cands[iPeak].seed[1] = seedVertex[1];
1068+
cands[iPeak].seed[2] = seedVertex[2];
1069+
}
1070+
10521071
const float bd2 = (beamX - vertex[0]) * (beamX - vertex[0]) + (beamY - vertex[1]) * (beamY - vertex[1]);
1053-
if (nKept < minContributors || !(bd2 < nSigmaCut)) {
1072+
if (fit.getNContributors() < minContributors || !(bd2 < nSigmaCut)) {
10541073
continue;
10551074
}
1056-
if (fineMaxDrift > 0.f && cands[p].fine &&
1075+
if (fineMaxDrift > 0.f && (isZPeakFine != nullptr ? isZPeakFine[k] : 0) &&
10571076
o2::gpu::GPUCommonMath::Abs(vertex[2] - seedVertex[2]) > fineMaxDrift) {
10581077
continue;
10591078
}
10601079

1061-
for (int j = wk.lo; j < wk.hi; ++j) {
1062-
const auto tj = t[j];
1063-
if (tk.isCompatible(tj)) {
1064-
const o2::its::Line& line = lines[idx[j]];
1065-
if (o2::its::Line::getDistance2FromPoint(line, seedVertex) < pairCut2) {
1066-
fit.addResidual(line, vertex);
1067-
}
1080+
for (int iLine = wk.lo + lane; iLine < wk.hi; iLine += gpu::WarpSize) {
1081+
const auto tj = t[iLine];
1082+
if (!tk.isCompatible(tj)) {
1083+
continue;
10681084
}
1085+
const o2::its::Line& line = lines[idx[iLine]];
1086+
if (!(o2::its::Line::getDistance2FromPoint(line, seedVertex) < pairCut2)) {
1087+
continue;
1088+
}
1089+
fit.addResidual(line, vertex);
10691090
}
1070-
1071-
cands[p].x = vertex[0];
1072-
cands[p].y = vertex[1];
1073-
cands[p].z = vertex[2];
1074-
for (int i = 0; i < 6; ++i) {
1075-
cands[p].rms2[i] = fit.getRMS2()[i];
1091+
fit.reduceResiduals<gpu::WarpSize>(nKept);
1092+
1093+
if (lane == 0) {
1094+
cands[iPeak].x = vertex[0];
1095+
cands[iPeak].y = vertex[1];
1096+
cands[iPeak].z = vertex[2];
1097+
for (int i = 0; i < 6; ++i) {
1098+
cands[iPeak].rms2[i] = fit.getRMS2()[i];
1099+
}
1100+
cands[iPeak].avgDist2 = fit.getAvgDistance2();
1101+
cands[iPeak].nGood = nGood;
1102+
cands[iPeak].time = fit.getTimeStamp();
1103+
cands[iPeak].size = fit.getNContributors();
1104+
cands[iPeak].ok = 1;
10761105
}
1077-
cands[p].avgDist2 = fit.getAvgDistance2();
1078-
cands[p].nGood = nGood;
1079-
cands[p].time = fit.getTimeStamp();
1080-
cands[p].size = nKept;
1081-
cands[p].ok = 1;
10821106
}
10831107
}
10841108

@@ -1721,6 +1745,13 @@ void TrackingKernels<NLayers>::fitPeaksHandler(const int* nPeaksDevice,
17211745
gpu::VertexCand* cands,
17221746
gpu::Stream& stream)
17231747
{
1748+
// the kernel reduces with shuffles over gpu::WarpSize lanes: a device with another warp size would silently sum the wrong lanes
1749+
int dev = 0, warpSize = 0;
1750+
GPUChkErrS(cudaGetDevice(&dev));
1751+
GPUChkErrS(cudaDeviceGetAttribute(&warpSize, cudaDevAttrWarpSize, dev));
1752+
if (warpSize != gpu::WarpSize) {
1753+
LOGP(fatal, "fitPeaksKernel compiled for warp size {} but the device runs {}", gpu::WarpSize, warpSize);
1754+
}
17241755
gpu::fitPeaksKernel<NLayers><<<gpu::gridBlocks(gpu::DefaultBlocksPerComputeUnit), gpu::GPUThreads, 0, stream.get()>>>(
17251756
nPeaksDevice, peakLineIdx, win, sortedSoa, lines, lineChi2, linePt, goodLineChi2Cut, goodLinePtCut, pairCut2, nSigmaCut, minContributors, beamX, beamY, isPeakFine, fineMaxDrift, cands);
17261757
}

‎Detectors/ITSMFT/ITS/tracking/include/ITStracking/ClusterLines.h‎

Lines changed: 116 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
#include <array>
1818
#ifndef GPUCA_GPUCODE
1919
#include <cmath>
20+
#else
21+
#include "GPUCommonAlgorithm.h" // warp reductions of WarpFit
2022
#endif
2123
#include "ITStracking/Cluster.h"
2224
#include "ITStracking/Cell.h"
@@ -141,27 +143,19 @@ GPUdi() bool getCellLineAtBeam(const CellSeed& cell, const float beamX, const fl
141143
return true;
142144
}
143145

144-
/// Least-squares vertex fit over a set of lines (the normal equations AX = -B).
145-
class ClusterLines final
146+
/// Normal equations AX = -B of the point closest to a set of lines, and their solution. Base of the
147+
/// sequential fit (ClusterLines) and of the device warp-parallel fit (WarpFit), which only differ in how they accumulate.
148+
class LineFit
146149
{
147150
public:
148-
GPUhdDefault() ClusterLines() = default;
149-
/// Fit over lines[lineIndices[i]]; lineIndices must be sorted and unique.
150-
ClusterLines(gsl::span<const int> lineIndices, gsl::span<const Line> lines);
151-
152-
/// Accumulate one line into the normal equations and the time estimate.
153-
GPUhdi() void add(const Line& line)
151+
/// Accumulate one line
152+
GPUhdi() bool add(const Line& line)
154153
{
155154
const double d0 = line.cosinesDirector[0], d1 = line.cosinesDirector[1], d2 = line.cosinesDirector[2];
156155
const double o0 = line.originPoint[0], o1 = line.originPoint[1], o2 = line.originPoint[2];
157156
const double det = d0 * d0 + d1 * d1 + d2 * d2; // == 1 for a normalised director
158157
if (det <= 0.) {
159-
return;
160-
}
161-
if (mNContributors <= 0) {
162-
mTime = line.mTime;
163-
} else {
164-
mTime += line.mTime;
158+
return false;
165159
}
166160
mA[0] += (det - d0 * d0) / det;
167161
mA[1] += (-d0 * d1) / det;
@@ -173,20 +167,19 @@ class ClusterLines final
173167
mB[0] += (d0 * dDotO - det * o0) / det;
174168
mB[1] += (d1 * dDotO - det * o1) / det;
175169
mB[2] += (d2 * dDotO - det * o2) / det;
176-
++mNContributors;
170+
return true;
177171
}
178172

179-
/// Solve the symmetric 3x3 system into an external vertex (= -A^-1 B)
173+
/// Solve the symmetric 3x3 system into vertex (= -A^-1 B); false if it is singular
180174
GPUhdi() bool solve(float vertex[3]) const
181175
{
182-
const double a = mA[0], b = mA[1], c = mA[2], d = mA[3], e = mA[4], f = mA[5];
183-
const double c00 = d * f - e * e;
184-
const double c01 = c * e - b * f;
185-
const double c02 = b * e - c * d;
186-
const double c11 = a * f - c * c;
187-
const double c12 = b * c - a * e;
188-
const double c22 = a * d - b * b;
189-
const double det = a * c00 + b * c01 + c * c02;
176+
const double c00 = mA[3] * mA[5] - mA[4] * mA[4];
177+
const double c01 = mA[2] * mA[4] - mA[1] * mA[5];
178+
const double c02 = mA[1] * mA[4] - mA[2] * mA[3];
179+
const double c11 = mA[0] * mA[5] - mA[2] * mA[2];
180+
const double c12 = mA[1] * mA[2] - mA[0] * mA[4];
181+
const double c22 = mA[0] * mA[3] - mA[1] * mA[1];
182+
const double det = mA[0] * c00 + mA[1] * c01 + mA[2] * c02;
190183
if (o2::gpu::GPUCommonMath::Abs(det) < 1.e-12) {
191184
return false;
192185
}
@@ -203,6 +196,49 @@ class ClusterLines final
203196
/// Solve into the stored vertex, setting the validity flag
204197
GPUhdi() void computeClusterCentroid() { mIsValid = solve(mVertex); }
205198

199+
GPUhdi() bool isValid() const noexcept { return mIsValid; }
200+
GPUhdi() const float* getVertex() const noexcept { return mVertex; }
201+
GPUhdi() int getSize() const noexcept { return mNContributors; }
202+
GPUhdi() int getNContributors() const noexcept { return mNContributors; }
203+
GPUhdi() const TimeEstBC& getTimeStamp() const noexcept { return mTime; }
204+
GPUhdi() float getR2() const noexcept { return (mVertex[0] * mVertex[0]) + (mVertex[1] * mVertex[1]); }
205+
GPUhdi() float getR() const noexcept { return o2::gpu::GPUCommonMath::Sqrt(getR2()); }
206+
GPUhdi() const float* getRMS2() const noexcept { return mRMS2; } // {XX, XY, YY, XZ, YZ, ZZ}
207+
GPUhdi() float getAvgDistance2() const noexcept { return mAvgDistance2; }
208+
209+
protected:
210+
double mA[6] = {0., 0., 0., 0., 0., 0.}; // AX=B, packed symmetric normal matrix
211+
double mB[3] = {0., 0., 0.}; // AX=B, right-hand side
212+
float mVertex[3] = {0.f, 0.f, 0.f}; // cluster centroid position
213+
float mRMS2[6] = {0.f, 0.f, 0.f, 0.f, 0.f, 0.f}; // mean DCA components about the vertex
214+
float mAvgDistance2 = 0.f; // mean squared distance about the vertex
215+
int mNContributors = 0; // lines accumulated into the normal equations
216+
bool mIsValid = false; // true if the linear system was solved successfully
217+
TimeEstBC mTime; // intersection of the contributors' time intervals
218+
};
219+
220+
/// Least-squares vertex fit over a set of lines (the normal equations AX = -B).
221+
class ClusterLines final : public LineFit
222+
{
223+
public:
224+
GPUhdDefault() ClusterLines() = default;
225+
/// Fit over lines[lineIndices[i]]; lineIndices must be sorted and unique.
226+
ClusterLines(gsl::span<const int> lineIndices, gsl::span<const Line> lines);
227+
228+
/// Accumulate one line into the normal equations and the time estimate.
229+
GPUhdi() void add(const Line& line)
230+
{
231+
if (!LineFit::add(line)) {
232+
return;
233+
}
234+
if (mNContributors <= 0) {
235+
mTime = line.mTime;
236+
} else {
237+
mTime += line.mTime;
238+
}
239+
++mNContributors;
240+
}
241+
206242
/// Running-mean update of the RMS2 and average distance about the stored vertex
207243
GPUhdi() void addResidual(const Line& line) { addResidual(line, mVertex); }
208244

@@ -220,28 +256,66 @@ class ClusterLines final
220256
mAvgDistance2 += (d2 - mAvgDistance2) / static_cast<float>(mResidualCount);
221257
}
222258

223-
GPUhdi() bool isValid() const noexcept { return mIsValid; }
224-
GPUhdi() const float* getVertex() const noexcept { return mVertex; }
225-
GPUhdi() const float* getRMS2() const noexcept { return mRMS2; } // {XX, XY, YY, XZ, YZ, ZZ}
226-
GPUhdi() float getAvgDistance2() const noexcept { return mAvgDistance2; }
227-
GPUhdi() int getSize() const noexcept { return mNContributors; }
228-
GPUhdi() int getNContributors() const noexcept { return mNContributors; }
229-
GPUhdi() const TimeEstBC& getTimeStamp() const noexcept { return mTime; }
230-
GPUhdi() float getR2() const noexcept { return (mVertex[0] * mVertex[0]) + (mVertex[1] * mVertex[1]); }
231-
GPUhdi() float getR() const noexcept { return o2::gpu::GPUCommonMath::Sqrt(getR2()); }
232259
bool operator==(const ClusterLines& rhs) const noexcept;
233260

234261
private:
235-
double mA[6] = {0., 0., 0., 0., 0., 0.}; // AX=B, packed symmetric normal matrix
236-
double mB[3] = {0., 0., 0.}; // AX=B, right-hand side
237-
float mVertex[3] = {0.f, 0.f, 0.f}; // cluster centroid position
238-
float mRMS2[6] = {0.f, 0.f, 0.f, 0.f, 0.f, 0.f};
239-
float mAvgDistance2 = 0.f;
240-
int mNContributors = 0;
241-
int mResidualCount = 0;
242-
bool mIsValid = false; // true if the linear system was solved successfully
243-
TimeEstBC mTime;
262+
int mResidualCount = 0; // lines folded into the running means
263+
};
264+
265+
#ifdef GPUCA_GPUCODE
266+
/// Device counterpart of ClusterLines: each lane of a warp add()s its own subset of lines, then reduce() leaves
267+
/// the full sums, contributor count and time intersection in every lane
268+
struct WarpFit : public LineFit {
269+
uint32_t tLo = 0, tHi = 0xffffffffu;
270+
271+
GPUdi() void add(const Line& l)
272+
{
273+
if (LineFit::add(l)) {
274+
tLo = o2::gpu::GPUCommonMath::Max(tLo, l.mTime.lower());
275+
tHi = o2::gpu::GPUCommonMath::Min(tHi, l.mTime.upper());
276+
++mNContributors;
277+
}
278+
}
279+
280+
template <int Width>
281+
GPUdi() void reduce()
282+
{
283+
for (int i = 0; i < 6; ++i) {
284+
mA[i] = warp_reduce_add<Width>(mA[i]);
285+
}
286+
for (int i = 0; i < 3; ++i) {
287+
mB[i] = warp_reduce_add<Width>(mB[i]);
288+
}
289+
tLo = warp_reduce_max<Width>(tLo);
290+
tHi = warp_reduce_min<Width>(tHi);
291+
mNContributors = warp_reduce_add<Width>(mNContributors);
292+
mTime = TimeEstBC{tLo, tHi - tLo};
293+
}
294+
295+
GPUdi() void addResidual(const Line& l, const float vertex[3])
296+
{
297+
float dca[6];
298+
Line::getDCAComponents(l, vertex, dca);
299+
for (int i = 0; i < 6; ++i) {
300+
rms2Sum[i] += dca[i];
301+
}
302+
dist2Sum += Line::getDistance2FromPoint(l, vertex);
303+
}
304+
305+
/// Means over the n lines of the whole warp, left in every lane
306+
template <int Width>
307+
GPUdi() void reduceResiduals(const int n)
308+
{
309+
for (int i = 0; i < 6; ++i) {
310+
mRMS2[i] = warp_reduce_add<Width>(rms2Sum[i]) / n;
311+
}
312+
mAvgDistance2 = warp_reduce_add<Width>(dist2Sum) / n;
313+
}
314+
315+
double rms2Sum[6] = {0., 0., 0., 0., 0., 0.};
316+
double dist2Sum = 0.;
244317
};
318+
#endif
245319

246320
} // namespace o2::its
247321
#endif /* O2_ITS_CLUSTERLINES_H */

0 commit comments

Comments
 (0)