From 60185dd305bbce0606f31046169557884d27617f Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Fri, 6 Nov 2026 08:54:19 +0100 Subject: [PATCH] GPU: wait for the O2 track count before sorting the O2 output tracks With GPUCA_SPECIALIZE_THRUST_SORTS (CUDA and HIP), the host-side launch of GPUTPCGMO2Output::sort takes the number of elements from the host copy of the merger's memory, processors()->tpcMerger.NOutputTracksTPCO2(). That count is computed by GPUTPCGMO2Output::prepare on the device, and TransferMemoryResourceLinkToHost brings it to the host asynchronously, on the same stream, right before the sort is launched. The event of the transfer was only waited for after the sort, so the sort very likely got the value from before prepare ran: 0, since RunTPCTrackingMerger clears the merger's memory on the host at its start, and the transfer is queued behind prepare. Then the O2 output tracks keep the order in which prepare wrote them. This goes back to b0155c276877, which moved the wait after the sort. With debugLevel >= 1 or serializeGPU the transfer is synchronous, which would hide it. The other sorts that read host counts, SortTracks and SortTracksQPt, wait for their transfer with CondWaitEvent first; do the same here, by waiting before the sort. The wait was already needed before the allocation of the O2 output that follows, so it costs no extra synchronisation. Found by reading the code, while porting these sorts to the Metal backend, which reads the count after draining the stream. Not tested on CUDA or HIP, there is no NVIDIA or AMD GPU here; the CPU build compiles. To check on a GPU: with debugLevel 0, outputTracksTPCO2 should come out ordered by the sort's comparator with this change, and not without it. --- GPU/GPUTracking/Global/GPUChainTrackingMerger.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GPU/GPUTracking/Global/GPUChainTrackingMerger.cxx b/GPU/GPUTracking/Global/GPUChainTrackingMerger.cxx index 7b27bf4b105b5..528a848acf389 100644 --- a/GPU/GPUTracking/Global/GPUChainTrackingMerger.cxx +++ b/GPU/GPUTracking/Global/GPUChainTrackingMerger.cxx @@ -305,9 +305,9 @@ int32_t GPUChainTracking::RunTPCTrackingMerger(bool synchronizeOutput) runKernel(GetGridAuto(0, deviceType)); } TransferMemoryResourceLinkToHost(RecoStep::TPCMerging, Merger.MemoryResMemory(), 0, &mEvents->single); + SynchronizeEventAndRelease(mEvents->single, doGPU); // The CUDA / HIP specialization of the sort reads nO2Tracks on the host runKernel(GetGridAuto(0, deviceType)); mRec->ReturnVolatileDeviceMemory(); - SynchronizeEventAndRelease(mEvents->single, doGPU); if (GetProcessingSettings().clearO2OutputFromGPU) { mRec->MakeFutureDeviceMemoryAllocationsVolatile();