Project
Loading...
Searching...
No Matches
GPUReconstructionCPUDevice.cxx
Go to the documentation of this file.
1// Copyright 2019-2026 CERN and copyright holders of ALICE O2.
2// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
3// All rights not expressly granted are reserved.
4//
5// This software is distributed under the terms of the GNU General Public
6// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
7//
8// In applying this license CERN does not waive the privileges and immunities
9// granted to it by virtue of its status as an Intergovernmental Organization
10// or submit itself to any jurisdiction.
11
14
18#include "GPUReconstructionKernelIncludes.h"
19#include "GPUDef.h"
20#include "GPUConstantMem.h"
21#include "GPUDefParametersRuntime.h"
22#include "GPULogging.h"
23
24#include <algorithm>
25#include <cstring>
26#include <stdexcept>
27#include <string>
28#include <utility>
29
30#ifndef _WIN32
31#include <dlfcn.h>
32#endif
33
34using namespace o2::gpu;
35
37
39{
40 if (mMaster == nullptr) {
42 *mParDevice = *mParCPU; // We run the CPU kernels on the "device", so we use the CPU kernel parameters
43 mParDevice->par_ALTERNATE_BORDER_SORT = 1; // Required by the GPU code path without RTC
44 mParDevice->par_CF_SCAN_WORKGROUP_SIZE = 1; // No block-wide scan with 1 thread per block, the clusterizer uses the CPU stream compaction
45 for (auto& v : mParDevice->par_LB_maxThreads) {
46 v = 1;
47 }
48 }
49 mDeviceBackendSettings->deviceType = DeviceType::CPUDEVICE;
50}
51
53{
54 Exit();
55 if (mMaster == nullptr) {
56 delete mInternals;
57 }
58}
59
60int32_t GPUReconstructionCPUDevice::GPUChkErrInternal(const int64_t retval, const char* file, int32_t line) const
61{
62 if (retval) {
63 GPUError("CPUDevice Error: %ld (%s:%d)", (int64_t)retval, file, line);
64 }
65 return retval != 0;
66}
67
69{
70 if (GetProcessingSettings().rtc.enable && GetProcessingSettings().rtctech.runTest == 2) {
71 genAndLoadRTC();
72 exit(0);
73 }
74
75 if (mMaster == nullptr) {
76 mMultiprocessorCount = std::max<int32_t>(1, mMaxHostThreads); // One device block runs as one TBB task
77 mThreadCount = 1;
78 mWarpSize = 1;
80 mDeviceName = "CPUDevice";
81 if (GetProcessingSettings().deviceTimers) {
82 if (GetProcessingSettings().debugLevel >= 2) {
83 GPUInfo("Disabling device timers for CPUDevice");
84 }
85 mProcessingSettings->deviceTimers = 0;
86 }
87
88 // The arena does not own threads, it limits the concurrency of the command queues and kernels to the configured number of host threads
89 mInternals->arena = std::make_unique<tbb::task_arena>(std::max<int32_t>(1, mMaxHostThreads));
90
91 mInternals->deviceMemory = alignedDefaultBufferAllocator<char>(mDeviceMemorySize);
92 mInternals->hostMemory = alignedDefaultBufferAllocator<char>(mHostMemorySize);
93 mInternals->constantMemory = alignedDefaultBufferAllocator<char>(sizeof(GPUConstantMem));
94 if ((mDeviceMemorySize && mInternals->deviceMemory == nullptr) || (mHostMemorySize && mInternals->hostMemory == nullptr) || mInternals->constantMemory == nullptr) {
95 GPUError("CPUDevice memory allocation error (trying %ld bytes device / %ld bytes host memory)", (int64_t)mDeviceMemorySize, (int64_t)mHostMemorySize);
96 return 1;
97 }
101 if (GetProcessingSettings().debugLevel >= 1) {
102 GPUInfo("Memory ptrs: Device (%ld bytes): %p - Host (%ld bytes): %p", (int64_t)mDeviceMemorySize, mDeviceMemoryBase, (int64_t)mHostMemorySize, mHostMemoryBase);
103 memset(mHostMemoryBase, 0xDD, mHostMemorySize);
105 }
106
107 if (GetProcessingSettings().rtc.enable) {
108 genAndLoadRTC();
109 }
110
111 GPUInfo("CPUDevice Initialisation successfull (%d blocks, %d streams, %ld / %ld bytes host / device memory, Constant memory %ld)", mMultiprocessorCount, mNStreams, (int64_t)mHostMemorySize, (int64_t)mDeviceMemorySize, (int64_t)sizeof(GPUConstantMem));
112 } else {
115 mThreadCount = master->mThreadCount;
116 mWarpSize = master->mWarpSize;
118 mDeviceName = master->mDeviceName;
120 mInternals = master->mInternals;
121 GPUInfo("CPUDevice Initialisation successfull (from master)");
122 }
123
124 mEventStorage.reserve(mEvents.size());
125 for (uint32_t i = 0; i < mEvents.size(); i++) {
126 mEventStorage.emplace_back(mEvents[i].size());
127 for (uint32_t j = 0; j < mEvents[i].size(); j++) {
128 mEvents[i][j].set(&mEventStorage[i][j]);
129 }
130 }
131
132 return 0;
133}
134
136{
138 mEventStorage.clear();
139
140 if (mMaster == nullptr) {
141#ifndef _WIN32
142 for (auto* lib : mInternals->rtcLibraries) {
143 dlclose(lib);
144 }
145#endif
146 mInternals->rtcLibraries.clear();
147 mInternals->rtcKernels.clear();
148 mInternals->arena.reset();
153 GPUInfo("CPUDevice Uninitialized");
154 }
155 mDeviceMemoryBase = nullptr;
156 mHostMemoryBase = nullptr;
157 mDeviceConstantMem = nullptr;
158
159 return 0;
160}
161
162GPUReconstructionCPUDeviceEvent* GPUReconstructionCPUDevice::getEvent(deviceEvent& ev)
163{
164 return ev.get<GPUReconstructionCPUDeviceEvent*>();
165}
166
168{
169 auto& q = queues[stream];
170 bool start = false;
171 {
172 std::lock_guard lock(q.mutex);
173 q.tasks.emplace_back(std::move(task));
174 if (!q.active) {
175 q.active = start = true;
176 }
177 }
178 if (start) {
180 }
181}
182
184{
185 arena->enqueue([this, stream]() { processQueue(stream); });
186}
187
189{
190 auto& q = queues[stream];
191 while (true) {
193 {
194 std::lock_guard lock(q.mutex);
195 if (q.tasks.empty()) {
196 q.active = false;
197 q.cv.notify_all();
198 return;
199 }
200 task = &q.tasks.front(); // Only this TBB task removes elements, and push_back does not invalidate references into a deque
201 }
202 while (task->nextWait < task->waits.size()) {
203 auto [e, value] = task->waits[task->nextWait];
204 std::lock_guard lock(e->mutex);
205 if (e->nCompleted < value) { // Do not block the TBB thread, suspend the queue, completeEvent will start it again
206 e->waiting.push_back({value, this, stream});
207 return;
208 }
209 task->nextWait++;
210 }
211 if (!error) {
212 try {
213 task->work();
214 } catch (const std::exception& ex) {
215 GPUError("CPUDevice: Error in command queue %d: %s", stream, ex.what());
216 error = 1;
217 }
218 }
219 if (task->record) {
220 completeEvent(task->record, task->recordValue); // Also after errors, so that nothing waits forever
221 }
222 std::lock_guard lock(q.mutex);
223 q.tasks.pop_front();
224 }
225}
226
228{
229 std::vector<GPUReconstructionCPUDeviceEvent::waitingQueue> resume;
230 {
231 std::lock_guard lock(e->mutex);
232 e->nCompleted = std::max(e->nCompleted, value);
233 for (auto it = e->waiting.begin(); it != e->waiting.end();) {
234 if (it->value <= e->nCompleted) {
235 resume.emplace_back(*it);
236 it = e->waiting.erase(it);
237 } else {
238 it++;
239 }
240 }
241 e->cv.notify_all();
242 }
243 for (auto& w : resume) {
244 w.internals->startQueue(w.stream);
245 }
246}
247
248void GPUReconstructionCPUDevice::EnqueueTask(int32_t stream, deviceEvent* evList, int32_t nEvents, deviceEvent* ev, std::function<void()>&& work)
249{
250 if (stream < 0 || stream >= mNStreams) {
251 throw std::runtime_error("CPUDevice: invalid stream " + std::to_string(stream));
252 }
254 if (evList) { // Capture the event states at enqueue time, such that the queue waits for the markers recorded so far, like cudaStreamWaitEvent
255 for (int32_t k = 0; k < nEvents; k++) {
256 auto* e = getEvent(evList[k]);
257 std::lock_guard lock(e->mutex);
258 task.waits.emplace_back(e, e->nRecorded);
259 }
260 }
261 if (ev) {
262 task.record = getEvent(*ev);
263 std::lock_guard lock(task.record->mutex);
264 task.recordValue = ++task.record->nRecorded;
265 }
266 task.work = std::move(work);
267 mInternals->enqueue(stream, std::move(task));
268}
269
271{
272 auto& q = mInternals->queues[stream];
273 std::unique_lock lock(q.mutex);
274 q.cv.wait(lock, [&q]() { return !q.active; });
275}
276
278{
279 for (uint32_t i = 0; i < mInternals->queues.size(); i++) {
281 }
282}
283
285{
286 for (int32_t i = 0; i < nEvents; i++) {
287 auto* e = getEvent(evList[i]);
288 std::unique_lock lock(e->mutex);
289 const uint64_t value = e->nRecorded;
290 e->cv.wait(lock, [e, value]() { return e->nCompleted >= value; });
291 }
292}
293
295{
296 if (nEvents) {
297 EnqueueTask(stream, evList, nEvents, nullptr, []() {});
298 }
299}
300
302{
303 for (int32_t i = 0; i < nEvents; i++) {
304 auto* e = getEvent(evList[i]);
305 std::lock_guard lock(e->mutex);
306 if (e->nCompleted < e->nRecorded) {
307 return false;
308 }
309 }
310 return true;
311}
312
314{
315 EnqueueTask(stream, nullptr, 0, ev, []() {});
316}
317
318int32_t GPUReconstructionCPUDevice::GPUDebug(const char* state, int32_t stream, bool force)
319{
320 if (mInternals->error) {
321 GPUError("CPUDevice Error while running (%s) (Stream %d)", state, stream);
322 return 1;
323 }
324 if (!force && GetProcessingSettings().debugLevel <= 0) {
325 return 0;
326 }
327 if (stream == -1) {
329 } else {
331 }
332 if (mInternals->error) {
333 GPUError("CPUDevice Error while synchronizing (%s) (Stream %d)", state, stream);
334 return 1;
335 }
336 if (GetProcessingSettings().debugLevel >= 3) {
337 GPUInfo("GPU Sync Done");
338 }
339 return 0;
340}
341
342size_t GPUReconstructionCPUDevice::GPUMemCpy(void* dst, const void* src, size_t size, int32_t stream, int32_t toGPU, deviceEvent* ev, deviceEvent* evList, int32_t nEvents)
343{
344 if (GetProcessingSettings().debugLevel >= 3) {
345 stream = -1;
346 }
347 if (stream == -1) {
349 if (size) {
350 memcpy(dst, src, size);
351 }
352 if (ev) {
353 auto* e = getEvent(*ev);
354 uint64_t value;
355 {
356 std::lock_guard lock(e->mutex);
357 value = ++e->nRecorded;
358 }
360 }
361 } else {
362 EnqueueTask(stream, evList, evList ? nEvents : 0, ev, [dst, src, size]() {
363 if (size) {
364 memcpy(dst, src, size);
365 }
366 });
367 }
368 if (GetProcessingSettings().serializeGPU & 2) {
369 GPUDebug(("GPUMemCpy " + std::to_string(toGPU)).c_str(), stream, true);
370 }
371 return size;
372}
373
375{
376 char* dst = (char*)mDeviceConstantMem + offset;
377 if (stream == -1) {
379 memcpy(dst, src, size);
380 } else {
381 // The source may be a temporary, the CUDA backend stages the copy too, so we take a copy for the asynchronous transfer
382 auto buffer = std::make_shared<std::vector<char>>((const char*)src, (const char*)src + size);
383 EnqueueTask(stream, nullptr, 0, ev, [dst, buffer]() { memcpy(dst, buffer->data(), buffer->size()); });
384 }
385 if (GetProcessingSettings().serializeGPU & 2) {
386 GPUDebug("WriteToConstantMemory", stream, true);
387 }
388 return size;
389}
390
391template <class T, int32_t I, typename... Args>
392inline void GPUReconstructionCPUDevice::runKernelBackend(const krnlSetupTime& _xyz, const Args&... args)
393{
394 auto& x = _xyz.x;
395 auto& y = _xyz.y;
396 auto& z = _xyz.z;
397 if (x.nThreads != 1) {
398 throw std::runtime_error("CPUDevice backend supports only kernels with nThreads = 1");
399 }
400 const uint32_t nBlocks = x.nBlocks;
401 const int32_t iSector = y.index;
402 GPUConstantMem* constantMem = mDeviceConstantMem;
403 using rtcKernel = void (*)(GPUConstantMem*, int32_t, uint32_t, uint32_t, uint32_t, Args...);
404 rtcKernel rtc = mInternals->rtcKernels.size() ? reinterpret_cast<rtcKernel>(mInternals->rtcKernels[GetKernelNum<T, I>()]) : nullptr;
405
406 // The kernel arguments are captured by value, since the kernel runs asynchronously in the command queue
407 EnqueueTask(x.stream, z.evList, z.evList ? z.nEvents : 0, z.ev, [nBlocks, iSector, constantMem, rtc, ... args = args]() {
408 tbb::this_task_arena::isolate([&] { // Do not pick up tasks of other command queues while waiting inside parallel_for
409 tbb::parallel_for(tbb::blocked_range<uint32_t>(0, nBlocks, 1), [&](const tbb::blocked_range<uint32_t>& r) {
410 if (rtc) {
411 rtc(constantMem, iSector, nBlocks, r.begin(), r.end(), args...);
412 } else {
413 for (uint32_t iBlock = r.begin(); iBlock < r.end(); iBlock++) {
414 typename T::GPUSharedMemory smem;
415 T::template Thread<I>(nBlocks, 1, iBlock, 0, smem, T::Processor(*constantMem)[iSector], args...);
416 }
417 }
418 });
419 });
420 });
421}
422
423template <>
424inline void GPUReconstructionCPUDevice::runKernelBackend<GPUMemClean16, 0>(const krnlSetupTime& _xyz, void* const& ptr, uint64_t const& size)
425{
426 EnqueueTask(_xyz.x.stream, _xyz.z.evList, _xyz.z.evList ? _xyz.z.nEvents : 0, _xyz.z.ev, [ptr, size]() { memset(ptr, 0, size); });
427}
428
429#include "GPUReconstructionProcessingKernels.inc"
431
432// Entry point of an RTC-compiled kernel, running the blocks [iBlockStart, iBlockEnd) with one thread per block. Must match the rtcKernel function pointer type in GPUReconstructionCPUDevice.cxx.
433#define GPUCA_CPUDEVICE_KRNL_RTC(x_class, x_attributes, x_arguments, x_forward, x_types, ...) \
434 extern "C" __attribute__((visibility("default"))) void GPUCA_M_CAT(krnl_, GPUCA_M_KRNL_NAME(x_class))(GPUConstantMem * pConstant, int32_t _iSector_internal, uint32_t nBlocks, uint32_t iBlockStart, uint32_t iBlockEnd GPUCA_M_STRIP(x_arguments)) \
435 { \
436 for (uint32_t iBlock = iBlockStart; iBlock < iBlockEnd; iBlock++) { \
437 typename GPUCA_M_STRIP_FIRST(x_class)::GPUSharedMemory smem; \
438 GPUCA_M_STRIP_FIRST(x_class)::template Thread<GPUCA_M_KRNL_NUM(x_class)>(nBlocks, 1, iBlock, 0, smem, GPUCA_M_STRIP_FIRST(x_class)::Processor(*pConstant)[_iSector_internal] GPUCA_M_STRIP(x_forward)); \
439 } \
440 }
441
442void GPUReconstructionCPUDevice::getRTCKernelCalls(std::vector<std::string>& kernels)
443{
444#define GPUCA_KRNL(...) kernels.emplace_back(GPUCA_M_STR(GPUCA_CPUDEVICE_KRNL_RTC(__VA_ARGS__)));
445#include "GPUReconstructionKernelList.h"
446#undef GPUCA_KRNL
447}
int32_t i
GPUReconstruction * GPUReconstruction_Create_CPUDEVICE(const GPUSettingsDeviceBackend &cfg)
uint32_t iSector
SurfaceTrackState state
uint32_t j
Definition RawData.h:0
TBranch * ptr
double num
GPUReconstructionCPUDevice(const GPUSettingsDeviceBackend &cfg)
int32_t GPUChkErrInternal(const int64_t retval, const char *file, int32_t line) const override
void StreamWaitForEvents(int32_t stream, deviceEvent *evList, int32_t nEvents=1) override
int32_t GPUDebug(const char *state="UNKNOWN", int32_t stream=-1, bool force=false) override
size_t WriteToConstantMemory(size_t offset, const void *src, size_t size, int32_t stream=-1, deviceEvent *ev=nullptr) override
void runKernelBackend(const krnlSetupTime &_xyz, const Args &... args)
void RecordMarker(deviceEvent *ev, int32_t stream) override
size_t GPUMemCpy(void *dst, const void *src, size_t size, int32_t stream, int32_t toGPU, deviceEvent *ev=nullptr, deviceEvent *evList=nullptr, int32_t nEvents=1) override
GPUReconstructionCPUDeviceInternals * mInternals
void SynchronizeEvents(deviceEvent *evList, int32_t nEvents=1) override
bool IsEventDone(deviceEvent *evList, int32_t nEvents=1) override
virtual void runKernelVirtual(const int num, const void *args)
std::vector< std::vector< deviceEvent > > mEvents
GPUConstantMem * mDeviceConstantMem
alignedDeleter< char, constants::GPU_BUFFER_ALIGNMENT > alignedDefaultBufferDeleter
std::unique_ptr< GPUSettingsDeviceBackend > mDeviceBackendSettings
std::unique_ptr< GPUSettingsProcessing > mProcessingSettings
const GPUSettingsProcessing & GetProcessingSettings() const
GLint GLenum GLint x
Definition glcorearb.h:403
GLenum src
Definition glcorearb.h:1767
GLuint buffer
Definition glcorearb.h:655
GLsizeiptr size
Definition glcorearb.h:659
const GLdouble * v
Definition glcorearb.h:832
GLint y
Definition glcorearb.h:270
GLsizei const GLfloat * value
Definition glcorearb.h:819
GLenum GLenum dst
Definition glcorearb.h:1767
GLintptr offset
Definition glcorearb.h:660
typedef void(APIENTRYP PFNGLCULLFACEPROC)(GLenum mode)
GLboolean r
Definition glcorearb.h:1233
GLuint start
Definition glcorearb.h:469
GLuint GLuint stream
Definition glcorearb.h:1806
GLubyte GLubyte GLubyte GLubyte w
Definition glcorearb.h:852
GLdouble GLdouble GLdouble z
Definition glcorearb.h:843
std::string to_string(gsl::span< T, Size > span)
Definition common.h:52
int32_t par_LB_maxThreads[$< LIST:LENGTH, $< TARGET_PROPERTY:O2_GPU_KERNELS, O2_GPU_KERNEL_NAMES > >]
void enqueue(int32_t stream, GPUReconstructionCPUDeviceTask &&task)
void completeEvent(GPUReconstructionCPUDeviceEvent *e, uint64_t value)
std::array< GPUReconstructionCPUDeviceQueue, constants::GPU_MAX_STREAMS > queues
std::vector< std::pair< GPUReconstructionCPUDeviceEvent *, uint64_t > > waits
const int nEvents
Definition test_Fifo.cxx:27