Project
Loading...
Searching...
No Matches
GPUReconstructionCPUDeviceGenRTC.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
16#include "GPULogging.h"
17
18#include <stdexcept>
19#include <string>
20
21#ifdef GPUCA_CPUDEVICE_RTC
22
24#include "GPUReconstructionKernelIncludes.h"
26#include "GPUParamRTC.h"
27#include "GPUDefParametersLoad.inc"
28#include "GPUNoFastMathKernels.h"
29#include "Framework/SHA1.h"
31#include "utils/timer.h"
32
33#include <cstdio>
34#include <cstring>
35#include <filesystem>
36#include <string_view>
37#include <vector>
38#include <dlfcn.h>
39#include <fcntl.h>
40#include <sys/stat.h>
41#include <unistd.h>
42
43QGET_LD_BINARY_SYMBOLS(GPUReconstructionCPUDevicertc_src);
44QGET_LD_BINARY_SYMBOLS(GPUReconstructionCPUDevicertc_command);
45QGET_LD_BINARY_SYMBOLS(GPUReconstructionCPUDevicertc_command_no_fast_math);
46
47using namespace o2::gpu;
48
49static constexpr const char* RTC_SRC_EXTENSION = ".cxx";
50static constexpr const char* RTC_BIN_EXTENSION = ".so";
51static constexpr const char* RTC_MAP_EXTENSION = ".map";
52
53static bool kernelWithoutRTC(const std::string& name) { return name == "GPUTPCCFCheckPadBaseline"; } // Device code requires more than 1 thread per block, use the host-compiled kernel
54
55int32_t GPUReconstructionCPUDevice::genRTC(std::string& filename, uint32_t& nCompile)
56{
57 const std::string_view rtcSource(_binary_GPUReconstructionCPUDevicertc_src_start, _binary_GPUReconstructionCPUDevicertc_src_len);
58 const std::string rtcparam = std::string("#define GPUCA_RTC_CODE\n") +
59 std::string(GetProcessingSettings().rtc.optSpecialCode ? "#define GPUCA_RTC_SPECIAL_CODE(...) __VA_ARGS__\n" : "#define GPUCA_RTC_SPECIAL_CODE(...)\n") +
60 std::string(GetProcessingSettings().rtc.optConstexpr ? "#define GPUCA_RTC_CONSTEXPR constexpr\n" : "#define GPUCA_RTC_CONSTEXPR\n") +
61 GPUParamRTC::generateRTCCode(param(), GetProcessingSettings().rtc.optConstexpr);
62 if (filename == "") {
63 filename = "/tmp/o2cagpu_rtc_cpudevice_";
64 }
65 filename += std::to_string(getpid());
66 filename += "_";
67 filename += std::to_string(rand());
68
69 std::vector<std::string> kernels;
70 getRTCKernelCalls(kernels);
71 for (uint32_t i = 0; i < kernels.size(); i++) {
72 if (kernelWithoutRTC(GetKernelName(i))) {
73 kernels[i].clear();
74 }
75 }
76 std::string kernelsall;
77 for (uint32_t i = 0; i < kernels.size(); i++) {
78 kernelsall += kernels[i] + "\n";
79 }
80
81 std::string baseCommand = (GetProcessingSettings().rtctech.prependCommand != "" ? (GetProcessingSettings().rtctech.prependCommand + " ") : "");
82 baseCommand += (getenv("O2_GPU_RTC_OVERRIDE_CMD") ? std::string(getenv("O2_GPU_RTC_OVERRIDE_CMD")) : std::string(_binary_GPUReconstructionCPUDevicertc_command_start, _binary_GPUReconstructionCPUDevicertc_command_len));
83 baseCommand += std::string(" ") + GetProcessingSettings().rtctech.overrideArchitecture;
84
85 if (GetProcessingSettings().rtctech.loadLaunchBoundsFromFile.size()) {
86 FILE* fp = fopen(GetProcessingSettings().rtctech.loadLaunchBoundsFromFile.c_str(), "rb");
87 if (fp == nullptr) {
88 throw std::runtime_error("Cannot open launch bounds parameter module file");
89 }
90 fseek(fp, 0, SEEK_END);
91 size_t size = ftell(fp);
92 if (size != sizeof(*mParDevice)) {
93 throw std::runtime_error("launch bounds parameter file has incorrect size");
94 }
95 fseek(fp, 0, SEEK_SET);
96 if (fread(mParDevice, 1, size, fp) != size) {
97 throw std::runtime_error("Error reading launch bounds parameter file");
98 }
99 fclose(fp);
100 }
101 const std::string launchBounds = o2::gpu::internal::GPUDefParametersExport(*mParDevice, true, 0) + "#define GPUCA_WARP_SIZE " + std::to_string(mWarpSize) + "\n";
102 if (GetProcessingSettings().rtctech.printLaunchBounds || GetProcessingSettings().debugLevel >= 3) {
103 GPUInfo("RTC Launch Bounds:\n%s", launchBounds.c_str());
104 if (GetProcessingSettings().rtctech.printLaunchBounds >= 2) {
105 return 1;
106 }
107 }
108
109 const std::string compilerVersions = getBackendVersions();
110
111 char shasource[21], shaparam[21], shacmd[21], shakernels[21], shabounds[21], shaversion[21];
112 if (GetProcessingSettings().rtc.cacheOutput) {
113 o2::framework::internal::SHA1(shasource, rtcSource.data(), rtcSource.size());
114 o2::framework::internal::SHA1(shaparam, rtcparam.c_str(), rtcparam.size());
115 o2::framework::internal::SHA1(shacmd, baseCommand.c_str(), baseCommand.size());
116 o2::framework::internal::SHA1(shakernels, kernelsall.c_str(), kernelsall.size());
117 o2::framework::internal::SHA1(shabounds, launchBounds.c_str(), launchBounds.size());
118 o2::framework::internal::SHA1(shaversion, compilerVersions.c_str(), compilerVersions.size());
119 }
120
121 nCompile = GetProcessingSettings().rtc.compilePerKernel ? kernels.size() : 1;
122 bool cacheLoaded = false;
123 int32_t fd = 0;
124 const std::string cacheFile = GetProcessingSettings().rtctech.cacheFolder + "/rtc.cpudevice.cache";
125 if (GetProcessingSettings().rtc.cacheOutput) {
126 if (GetProcessingSettings().rtctech.cacheFolder != ".") {
127 std::filesystem::create_directories(GetProcessingSettings().rtctech.cacheFolder);
128 }
129 if (GetProcessingSettings().rtctech.cacheMutex) {
130 mode_t mask = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
131 fd = open((GetProcessingSettings().rtctech.cacheFolder + "/cache.lock").c_str(), O_RDWR | O_CREAT | O_CLOEXEC, mask);
132 if (fd == -1) {
133 throw std::runtime_error("Error opening rtc cache mutex lock file");
134 }
135 fchmod(fd, mask);
136 if (lockf(fd, F_LOCK, 0)) {
137 throw std::runtime_error("Error locking rtc cache mutex file");
138 }
139 }
140
141 FILE* fp = fopen(cacheFile.c_str(), "rb");
142 char sharead[20];
143 if (fp) {
144 size_t len;
145 while (true) {
146 auto checkSHA = [&](const char* shacmp, const char* name) {
147 if (fread(sharead, 1, 20, fp) != 20) {
148 throw std::runtime_error("Cache file corrupt");
149 }
150 if (!GetProcessingSettings().rtctech.ignoreCacheValid && memcmp(sharead, shacmp, 20)) {
151 GPUInfo("Cache file content outdated (%s)", name);
152 return 1;
153 }
154 return 0;
155 };
156 if (checkSHA(shasource, "source") ||
157 checkSHA(shaparam, "param") ||
158 checkSHA(shacmd, "command line") ||
159 checkSHA(shakernels, "kernel definitions") ||
160 checkSHA(shabounds, "launch bounds") ||
161 checkSHA(shaversion, "compiler versions")) {
162 break;
163 }
164 GPUSettingsProcessingRTC cachedSettings;
165 static_assert(std::is_trivially_copyable_v<GPUSettingsProcessingRTC> == true, "GPUSettingsProcessingRTC must be POD");
166 if (fread(&cachedSettings, sizeof(cachedSettings), 1, fp) != 1) {
167 throw std::runtime_error("Cache file corrupt");
168 }
169 if (!GetProcessingSettings().rtctech.ignoreCacheValid && !(cachedSettings == GetProcessingSettings().rtc)) {
170 GPUInfo("Cache file content outdated (rtc parameters)");
171 break;
172 }
173 std::vector<char> buffer;
174 for (uint32_t i = 0; i < nCompile; i++) {
175 if (fread(&len, sizeof(len), 1, fp) != 1) {
176 throw std::runtime_error("Cache file corrupt");
177 }
178 buffer.resize(len);
179 if (fread(buffer.data(), 1, len, fp) != len) {
180 throw std::runtime_error("Cache file corrupt");
181 }
182 FILE* fp2 = fopen((filename + "_" + std::to_string(i) + RTC_BIN_EXTENSION).c_str(), "w+b");
183 if (fp2 == nullptr) {
184 throw std::runtime_error("Cannot open tmp file");
185 }
186 if (fwrite(buffer.data(), 1, len, fp2) != len) {
187 throw std::runtime_error("Error writing file");
188 }
189 fclose(fp2);
190 }
191 GPUInfo("Using RTC cache file");
192 cacheLoaded = true;
193 break;
194 };
195 fclose(fp);
196 }
197 }
198 if (!cacheLoaded) {
199 if (GetProcessingSettings().debugLevel >= 0) {
200 GPUInfo("Starting CPUDevice RTC Compilation");
201 }
202 HighResTimer rtcTimer;
203 rtcTimer.ResetStart();
204 tbb::parallel_for<uint32_t>(0, nCompile, [&](auto i) {
205 const std::string fileBase = filename + "_" + std::to_string(i);
206 if (GetProcessingSettings().debugLevel >= 3) {
207 printf("Compiling %s\n", (fileBase + RTC_SRC_EXTENSION).c_str());
208 }
209 FILE* fp = fopen((fileBase + RTC_SRC_EXTENSION).c_str(), "w+b");
210 if (fp == nullptr) {
211 throw std::runtime_error("Error opening file");
212 }
213
214 const std::string& kernel = GetProcessingSettings().rtc.compilePerKernel ? kernels[i] : kernelsall;
215
216 bool deterministic = GetProcessingSettings().rtc.deterministic || (GetProcessingSettings().rtc.compilePerKernel && o2::gpu::internal::noFastMathKernels.find(GetKernelName(i)) != o2::gpu::internal::noFastMathKernels.end());
217 const std::string deterministicStr = std::string(deterministic ? "#define GPUCA_DETERMINISTIC_CODE(det, indet) det\n" : "#define GPUCA_DETERMINISTIC_CODE(det, indet) indet\n");
218
219 if (fwrite(deterministicStr.c_str(), 1, deterministicStr.size(), fp) != deterministicStr.size() ||
220 fwrite(rtcparam.c_str(), 1, rtcparam.size(), fp) != rtcparam.size() ||
221 fwrite(launchBounds.c_str(), 1, launchBounds.size(), fp) != launchBounds.size() ||
222 fwrite(rtcSource.data(), 1, rtcSource.size(), fp) != rtcSource.size() ||
223 fwrite(kernel.c_str(), 1, kernel.size(), fp) != kernel.size()) {
224 throw std::runtime_error("Error writing file");
225 }
226 fclose(fp);
227
228 // Export only the kernel entry points, everything else stays local to the RTC library to avoid symbol collisions
229 fp = fopen((fileBase + RTC_MAP_EXTENSION).c_str(), "w+b");
230 if (fp == nullptr || fprintf(fp, "{ global: krnl_*; local: *; };\n") < 0) {
231 throw std::runtime_error("Error writing version script");
232 }
233 fclose(fp);
234
235 std::string command = baseCommand;
236 if (deterministic) {
237 command += std::string(" ") + std::string(_binary_GPUReconstructionCPUDevicertc_command_no_fast_math_start, _binary_GPUReconstructionCPUDevicertc_command_no_fast_math_len);
238 }
239 command += " -Wl,--version-script=" + fileBase + RTC_MAP_EXTENSION + " -x c++ " + fileBase + RTC_SRC_EXTENSION + " -o " + fileBase + RTC_BIN_EXTENSION;
240 if (GetProcessingSettings().debugLevel < 0) {
241 command += " &> /dev/null";
242 } else if (GetProcessingSettings().debugLevel < 2) {
243 command += " > /dev/null";
244 }
245 if (GetProcessingSettings().debugLevel >= 3) {
246 printf("Running command %s\n", command.c_str());
247 }
248 if (system(command.c_str())) {
249 if (GetProcessingSettings().debugLevel >= 3) {
250 printf("Source code file: %s", (fileBase + RTC_SRC_EXTENSION).c_str());
251 }
252 throw std::runtime_error("Error during CPUDevice RTC compilation");
253 } // clang-format off
254 }, tbb::simple_partitioner()); // clang-format on
255 if (GetProcessingSettings().debugLevel >= 0) {
256 GPUInfo("RTC Compilation finished (%f seconds)", rtcTimer.GetCurrentElapsedTime());
257 }
258 if (GetProcessingSettings().rtc.cacheOutput) {
259 FILE* fp = fopen(cacheFile.c_str(), "w+b");
260 if (fp == nullptr) {
261 throw std::runtime_error("Cannot open cache file for writing");
262 }
263 GPUInfo("Storing RTC compilation result in cache file");
264
265 if (fwrite(shasource, 1, 20, fp) != 20 ||
266 fwrite(shaparam, 1, 20, fp) != 20 ||
267 fwrite(shacmd, 1, 20, fp) != 20 ||
268 fwrite(shakernels, 1, 20, fp) != 20 ||
269 fwrite(shabounds, 1, 20, fp) != 20 ||
270 fwrite(shaversion, 1, 20, fp) != 20 ||
271 fwrite(&GetProcessingSettings().rtc, sizeof(GetProcessingSettings().rtc), 1, fp) != 1) {
272 throw std::runtime_error("Error writing cache file");
273 }
274
275 std::vector<char> buffer;
276 for (uint32_t i = 0; i < nCompile; i++) {
277 FILE* fp2 = fopen((filename + "_" + std::to_string(i) + RTC_BIN_EXTENSION).c_str(), "rb");
278 if (fp2 == nullptr) {
279 throw std::runtime_error("Cannot open RTC library file");
280 }
281 fseek(fp2, 0, SEEK_END);
282 size_t size = ftell(fp2);
283 buffer.resize(size);
284 fseek(fp2, 0, SEEK_SET);
285 if (fread(buffer.data(), 1, size, fp2) != size) {
286 throw std::runtime_error("Error reading RTC library file");
287 }
288 fclose(fp2);
289
290 if (fwrite(&size, sizeof(size), 1, fp) != 1 ||
291 fwrite(buffer.data(), 1, size, fp) != size) {
292 throw std::runtime_error("Error writing cache file");
293 }
294 }
295 fclose(fp);
296 }
297 }
298 if (GetProcessingSettings().rtc.cacheOutput && GetProcessingSettings().rtctech.cacheMutex) {
299 if (lockf(fd, F_ULOCK, 0)) {
300 throw std::runtime_error("Error unlocking RTC cache mutex file");
301 }
302 close(fd);
303 }
304
305 return 0;
306}
307
308void GPUReconstructionCPUDevice::genAndLoadRTC()
309{
310 std::string filename = "";
311 uint32_t nCompile = 0;
312 if (genRTC(filename, nCompile)) {
313 throw std::runtime_error("Runtime compilation failed");
314 }
315 for (uint32_t i = 0; i < nCompile; i++) {
316 const std::string fileBase = filename + "_" + std::to_string(i);
317 if (GetProcessingSettings().rtctech.runTest != 2) {
318 void* lib = dlopen((fileBase + RTC_BIN_EXTENSION).c_str(), RTLD_NOW | RTLD_LOCAL);
319 if (lib == nullptr) {
320 throw std::runtime_error(std::string("Error loading CPUDevice RTC library: ") + dlerror());
321 }
322 mInternals->rtcLibraries.emplace_back(lib);
323 }
324 if (!GetProcessingSettings().rtctech.keepTempFiles) {
325 remove((fileBase + RTC_SRC_EXTENSION).c_str());
326 remove((fileBase + RTC_BIN_EXTENSION).c_str());
327 remove((fileBase + RTC_MAP_EXTENSION).c_str());
328 }
329 }
330 if (GetProcessingSettings().rtctech.runTest == 2) {
331 return;
332 }
333
334 const bool perKernel = GetProcessingSettings().rtc.compilePerKernel;
336 uint32_t j = 0;
337#define GPUCA_KRNL(x_class, ...) \
338 if (GetKernelNum<GPUCA_M_KRNL_TEMPLATE(x_class)>() != j) { \
339 GPUFatal("kernel numbers out of sync"); \
340 } \
341 mInternals->rtcKernels[j] = dlsym(mInternals->rtcLibraries[perKernel ? j : 0], GPUCA_M_STR(GPUCA_M_CAT(krnl_, GPUCA_M_KRNL_NAME(x_class)))); \
342 if (mInternals->rtcKernels[j] == nullptr && !kernelWithoutRTC(GetKernelName(j))) { \
343 GPUFatal("Error loading RTC kernel %s", GPUCA_M_STR(GPUCA_M_CAT(krnl_, GPUCA_M_KRNL_NAME(x_class)))); \
344 } \
345 j++;
346#include "GPUReconstructionKernelList.h"
347#undef GPUCA_KRNL
348 if (GetProcessingSettings().debugLevel >= 1) {
349 GPUInfo("Loaded %u CPUDevice RTC kernels from %u libraries", j, (uint32_t)mInternals->rtcLibraries.size());
350 }
351}
352
353#else // GPUCA_CPUDEVICE_RTC
354
355using namespace o2::gpu;
356
357int32_t GPUReconstructionCPUDevice::genRTC(std::string& filename, uint32_t& nCompile)
358{
359 throw std::runtime_error("CPUDevice backend compiled without RTC support (requires Linux)");
360}
361
362void GPUReconstructionCPUDevice::genAndLoadRTC()
363{
364 throw std::runtime_error("CPUDevice backend compiled without RTC support (requires Linux)");
365}
366
367#endif // GPUCA_CPUDEVICE_RTC
int32_t i
uint32_t j
Definition RawData.h:0
double GetCurrentElapsedTime(bool reset=false)
Definition timer.cxx:117
void ResetStart()
Definition timer.cxx:70
GPUReconstructionCPUDeviceInternals * mInternals
static const char * GetKernelName()
const GPUSettingsProcessing & GetProcessingSettings() const
GLsizei const GLchar *const * string
Definition glcorearb.h:809
GLuint buffer
Definition glcorearb.h:655
GLsizeiptr size
Definition glcorearb.h:659
GLuint const GLchar * name
Definition glcorearb.h:781
GLenum GLenum GLsizei len
Definition glcorearb.h:4232
GLenum GLfloat param
Definition glcorearb.h:271
GLint GLuint mask
Definition glcorearb.h:291
Polygon< T > close(Polygon< T > polygon)
Definition Polygon.h:126
std::string to_string(gsl::span< T, Size > span)
Definition common.h:52
std::string filename()
#define QGET_LD_BINARY_SYMBOLS(filename)