@@ -760,6 +760,63 @@ GPUg() void __launch_bounds__(GPUThreads, (std::is_same_v<CurrentSeed, CellSeed>
760760 }
761761}
762762
763+ // / Sort key that orders seeds by azimuth without mixing hit-layer patterns.
764+ template <int NLayers>
765+ GPUg () void __launch_bounds__ (GPUThreads, MinBlocks.compileLookupTable) computeTrackSeedSortKeysKernel(
766+ const TrackSeed<NLayers>* trackSeeds,
767+ const unsigned int nSeeds,
768+ unsigned int * keys)
769+ {
770+ static_assert (NLayers < 32 , " the hit-layer pattern must leave room for the azimuth bits" );
771+ constexpr int PhiShift{32 - NLayers};
772+ constexpr unsigned int PhiMask{(1u << PhiShift) - 1u };
773+ for (unsigned int iSeed = blockIdx .x * blockDim .x + threadIdx .x ; iSeed < nSeeds; iSeed += blockDim .x * gridDim .x ) {
774+ const auto & seed = trackSeeds[iSeed];
775+ const unsigned int hitPattern = seed.getHitLayerMask ().value ();
776+ const float phi = seed.getPhiPos (); // [0, 2pi)
777+ const unsigned int phiBin = static_cast <unsigned int >(phi * (static_cast <float >(PhiMask) / o2::constants::math::TwoPI)) & PhiMask;
778+ keys[iSeed] = (hitPattern << PhiShift) | phiBin;
779+ }
780+ }
781+
782+ // / Sort key that orders neighbour candidates by the cluster their fit will read.
783+ GPUg () void __launch_bounds__ (GPUThreads, MinBlocks.compileLookupTable) computeNeighbourCandidateSortKeysKernel(
784+ const int defaultCellTopologyId,
785+ const int * currentCellTopologyIds,
786+ CellSeed** allCellSeeds,
787+ CellNeighbour** neighbours,
788+ const NeighbourCandidate* candidates,
789+ const int nCandidates,
790+ unsigned int * keys)
791+ {
792+ constexpr unsigned int ClusterMask{0x07FFFFFFu };
793+ for (int iCandidate = blockIdx .x * blockDim .x + threadIdx .x ; iCandidate < nCandidates; iCandidate += blockDim .x * gridDim .x ) {
794+ const NeighbourCandidate candidate = candidates[iCandidate];
795+ const int cellTopologyId = currentCellTopologyIds == nullptr ? defaultCellTopologyId : currentCellTopologyIds[candidate.currentCell ];
796+ const auto & neighbourRef = neighbours[cellTopologyId][candidate.neighbourEntry ];
797+ const auto & neighbourCell = allCellSeeds[neighbourRef.cellTopology ][neighbourRef.cell ];
798+ keys[iCandidate] = (static_cast <unsigned int >(neighbourCell.getInnerLayer ()) << 27 ) |
799+ (static_cast <unsigned int >(neighbourCell.getFirstClusterIndex ()) & ClusterMask);
800+ }
801+ }
802+
803+ // / Order a candidate list in place by the hit each fit will read.
804+ void sortNeighbourCandidates (const int defaultCellTopologyId,
805+ const int * currentCellTopologyIds,
806+ CellSeed** allCellSeeds,
807+ CellNeighbour** neighbours,
808+ NeighbourCandidate* candidates,
809+ const int nCandidates,
810+ o2::its::ExternalAllocator* alloc)
811+ {
812+ auto keys = TypedAllocator<unsigned int >(alloc).allocate (nCandidates);
813+ auto policy = THRUST_NAMESPACE::par_nosync (TypedAllocator<char >(alloc)).on (Stream::DefaultStream);
814+ computeNeighbourCandidateSortKeysKernel<<<gridBlocks(ResidentBlocks.compileLookupTable), GPUThreads, 0 , Stream::DefaultStream>>> (
815+ defaultCellTopologyId, currentCellTopologyIds, allCellSeeds, neighbours, candidates, nCandidates,
816+ thrust::raw_pointer_cast (keys));
817+ thrust::stable_sort_by_key (policy, keys, keys + nCandidates, thrust::device_ptr<NeighbourCandidate>(candidates));
818+ }
819+
763820} // namespace gpu
764821
765822template <int NLayers>
@@ -1197,6 +1254,7 @@ void TrackingKernels<NLayers>::processNeighboursHandler(const int startLevel,
11971254 });
11981255
11991256 if (nCandidates > 0 ) {
1257+ gpu::sortNeighbourCandidates (topologyId, levelCellTopologyIds, allCellSeeds, neighbours, candidatePtr, nCandidates, alloc);
12001258 gpu::fitNeighbourCandidatesKernel<NLayers, LevelSeed><<<neighbourGrid, gpu::GPUThreads>>> (
12011259 topologyId, allCellSeeds, levelSeeds, levelCellTopologyIds,
12021260 candidatePtr, candidateCounterPtr, candidateCapacity, neighbours,
@@ -1300,6 +1358,26 @@ int TrackingKernels<NLayers>::computeTrackSeedHandler(TrackSeed<NLayers>* trackS
13001358{
13011359 GPUChkErrS (cudaMemsetAsync (outputCounter, 0 , sizeof (int ), gpu::Stream::DefaultStream));
13021360
1361+ if (nSeeds > 1 ) { // Group the seeds by hit-layer pattern before fitting them
1362+ constexpr uint64_t SortTag = qStr2Tag (" ITS_TSSK" );
1363+ alloc->pushTagOnStack (SortTag);
1364+ auto allocKey = gpu::TypedAllocator<unsigned int >(alloc);
1365+ auto allocIndex = gpu::TypedAllocator<int >(alloc);
1366+ auto allocSeed = gpu::TypedAllocator<TrackSeed<NLayers>>(alloc);
1367+ auto keys = allocKey.allocate (nSeeds);
1368+ auto order = allocIndex.allocate (nSeeds);
1369+ auto sortedSeeds = allocSeed.allocate (nSeeds);
1370+ auto sort_policy = THRUST_NAMESPACE::par_nosync (gpu::TypedAllocator<char >(alloc)).on (gpu::Stream::DefaultStream);
1371+ gpu::computeTrackSeedSortKeysKernel<NLayers><<<gpu::gridBlocks(gpu::ResidentBlocks.compileLookupTable), gpu::GPUThreads, 0 , gpu::Stream::DefaultStream>>> (
1372+ trackSeeds, nSeeds, thrust::raw_pointer_cast (keys));
1373+ thrust::sequence (sort_policy, order, order + nSeeds);
1374+ thrust::stable_sort_by_key (sort_policy, keys, keys + nSeeds, order);
1375+ thrust::gather (sort_policy, order, order + nSeeds, thrust::device_ptr<TrackSeed<NLayers>>(trackSeeds), sortedSeeds);
1376+ GPUChkErrS (cudaMemcpyAsync (trackSeeds, thrust::raw_pointer_cast (sortedSeeds), nSeeds * sizeof (TrackSeed<NLayers>), cudaMemcpyDeviceToDevice, gpu::Stream::DefaultStream));
1377+ GPUChkErrS (cudaStreamSynchronize (gpu::Stream::DefaultStream));
1378+ alloc->popTagOffStack (SortTag);
1379+ }
1380+
13031381 // track follower is compiled out of the kernel when no iteration asks for it
13041382 const auto launchFit = [&](auto extendTracks) {
13051383 gpu::fitTrackSeedsKernel<NLayers, decltype (extendTracks)::value><<<gpu::gridBlocks(decltype (extendTracks)::value ? gpu::ResidentBlocks.fitTrackSeedsExtended
0 commit comments