Skip to content

Commit 602edbf

Browse files
committed
GPU: Merge RTC generation for CUDA / HIP / CPUDevice into a helper include file
1 parent 7570972 commit 602edbf

3 files changed

Lines changed: 363 additions & 481 deletions

File tree

Lines changed: 309 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,309 @@
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+
12+
/// \file GPUReconstructionRTCHelpers.inc
13+
/// \author David Rohr
14+
15+
// Backend-independent helpers for the genRTC functions of the RTC backends, include only once per backend source file
16+
17+
#include "GPUSettings.h"
18+
#include "GPUParamRTC.h"
19+
#include "GPUDefParametersLoad.inc"
20+
#include "GPUNoFastMathKernels.h"
21+
#include "GPULogging.h"
22+
#include "Framework/SHA1.h"
23+
#include "utils/timer.h"
24+
25+
#include <array>
26+
#include <cstdio>
27+
#include <cstring>
28+
#include <filesystem>
29+
#include <stdexcept>
30+
#include <string>
31+
#include <string_view>
32+
#include <vector>
33+
#include <fcntl.h>
34+
#include <sys/stat.h>
35+
#include <unistd.h>
36+
#include <oneapi/tbb.h>
37+
38+
namespace o2::gpu::internal::rtc
39+
{
40+
41+
static std::string paramCode(const GPUSettingsProcessing& proc, const GPUParam& param) // RTC defines and parameters, common to all compilations
42+
{
43+
return std::string("#define GPUCA_RTC_CODE\n") +
44+
(proc.rtc.optSpecialCode ? "#define GPUCA_RTC_SPECIAL_CODE(...) __VA_ARGS__\n" : "#define GPUCA_RTC_SPECIAL_CODE(...)\n") +
45+
(proc.rtc.optConstexpr ? "#define GPUCA_RTC_CONSTEXPR constexpr\n" : "#define GPUCA_RTC_CONSTEXPR\n") +
46+
GPUParamRTC::generateRTCCode(param, proc.rtc.optConstexpr);
47+
}
48+
49+
static void tmpFilename(std::string& filename, const char* defaultPrefix)
50+
{
51+
filename = (filename == "" ? defaultPrefix : filename) + std::to_string(getpid()) + "_" + std::to_string(rand());
52+
}
53+
54+
static std::string joinKernels(const std::vector<std::string>& kernels)
55+
{
56+
std::string retVal;
57+
for (const auto& kernel : kernels) {
58+
retVal += kernel + "\n";
59+
}
60+
return retVal;
61+
}
62+
63+
static std::string baseCommand(const GPUSettingsProcessing& proc, std::string_view embeddedCommand) // Prepended command + compiler command (embedded or from O2_GPU_RTC_OVERRIDE_CMD)
64+
{
65+
const char* overrideCmd = getenv("O2_GPU_RTC_OVERRIDE_CMD");
66+
return (proc.rtctech.prependCommand != "" ? (proc.rtctech.prependCommand + " ") : "") + (overrideCmd ? std::string(overrideCmd) : std::string(embeddedCommand));
67+
}
68+
69+
static void loadLaunchBounds(const GPUSettingsProcessing& proc, GPUDefParameters& par)
70+
{
71+
if (proc.rtctech.loadLaunchBoundsFromFile.size() == 0) {
72+
return;
73+
}
74+
FILE* fp = fopen(proc.rtctech.loadLaunchBoundsFromFile.c_str(), "rb");
75+
if (fp == nullptr) {
76+
throw std::runtime_error("Cannot open launch bounds parameter module file");
77+
}
78+
fseek(fp, 0, SEEK_END);
79+
size_t size = ftell(fp);
80+
if (size != sizeof(par)) {
81+
throw std::runtime_error("launch bounds parameter file has incorrect size");
82+
}
83+
fseek(fp, 0, SEEK_SET);
84+
if (fread(&par, 1, size, fp) != size) {
85+
throw std::runtime_error("Error reading launch bounds parameter file");
86+
}
87+
fclose(fp);
88+
}
89+
90+
static std::string launchBoundsCode(const GPUSettingsProcessing& proc, const GPUDefParameters& par, int32_t minBlockFactor, uint32_t warpSize) // Prints the launch bounds if requested
91+
{
92+
std::string retVal = GPUDefParametersExport(par, true, minBlockFactor) + "#define GPUCA_WARP_SIZE " + std::to_string(warpSize) + "\n";
93+
if (proc.rtctech.printLaunchBounds || proc.debugLevel >= 3) {
94+
GPUInfo("RTC Launch Bounds:\n%s", retVal.c_str());
95+
}
96+
return retVal;
97+
}
98+
99+
static bool deterministic(const GPUSettingsProcessing& proc, const std::string& kernelName) // Whether compilation of kernel (or of all kernels with !compilePerKernel) uses deterministic code and no fast math
100+
{
101+
return proc.rtc.deterministic || (proc.rtc.compilePerKernel && noFastMathKernels.find(kernelName) != noFastMathKernels.end());
102+
}
103+
104+
static void writeSource(const std::string& file, bool deterministic, const std::string& param, const std::string& launchBounds, std::string_view source, const std::string& kernel)
105+
{
106+
const std::string deterministicStr = deterministic ? "#define GPUCA_DETERMINISTIC_CODE(det, indet) det\n" : "#define GPUCA_DETERMINISTIC_CODE(det, indet) indet\n";
107+
FILE* fp = fopen(file.c_str(), "w+b");
108+
if (fp == nullptr) {
109+
throw std::runtime_error("Error opening file");
110+
}
111+
for (std::string_view part : {std::string_view(deterministicStr), std::string_view(param), std::string_view(launchBounds), source, std::string_view(kernel)}) {
112+
if (fwrite(part.data(), 1, part.size(), fp) != part.size()) {
113+
throw std::runtime_error("Error writing file");
114+
}
115+
}
116+
fclose(fp);
117+
}
118+
119+
static void runCommand(const GPUSettingsProcessing& proc, std::string command, const std::string& sourceFile, const char* errorMessage) // Runs the compiler command, output suppressed depending on the debug level
120+
{
121+
if (proc.debugLevel < 0) {
122+
command += " &> /dev/null";
123+
} else if (proc.debugLevel < 2) {
124+
command += " > /dev/null";
125+
}
126+
if (proc.debugLevel >= 3) {
127+
printf("Running command %s\n", command.c_str());
128+
}
129+
if (system(command.c_str())) {
130+
if (proc.debugLevel >= 3) {
131+
printf("Source code file: %s", sourceFile.c_str());
132+
}
133+
throw std::runtime_error(errorMessage);
134+
}
135+
}
136+
137+
template <class T>
138+
static void compile(const GPUSettingsProcessing& proc, uint32_t nCompile, const char* backend, T&& compileOne) // Runs compileOne(i) for all compilations in parallel
139+
{
140+
if (proc.debugLevel >= 0) {
141+
GPUInfo("Starting %s RTC Compilation", backend);
142+
}
143+
HighResTimer rtcTimer;
144+
rtcTimer.ResetStart();
145+
tbb::parallel_for<uint32_t>(0, nCompile, compileOne, tbb::simple_partitioner());
146+
if (proc.debugLevel >= 0) {
147+
GPUInfo("RTC Compilation finished (%f seconds)", rtcTimer.GetCurrentElapsedTime());
148+
}
149+
}
150+
151+
class Cache // RTC cache file (if rtc.cacheOutput), locked while the object exists. Usage: if (!cache.load(...)) { compile; cache.store(...); }
152+
{
153+
public:
154+
static constexpr uint32_t N_SHA = 6;
155+
using Inputs = std::array<std::string_view, N_SHA>; // source, param, command line, kernel definitions, launch bounds, compiler versions
156+
157+
Cache(const GPUSettingsProcessing& proc, const char* cacheName, const Inputs& inputs) : mProc(proc), mFile(proc.rtctech.cacheFolder + "/" + cacheName)
158+
{
159+
if (!mProc.rtc.cacheOutput) {
160+
return;
161+
}
162+
for (uint32_t i = 0; i < N_SHA; i++) {
163+
o2::framework::internal::SHA1(mSHA[i], inputs[i].data(), inputs[i].size());
164+
}
165+
if (mProc.rtctech.cacheFolder != ".") {
166+
std::filesystem::create_directories(mProc.rtctech.cacheFolder);
167+
}
168+
if (mProc.rtctech.cacheMutex) {
169+
mode_t mask = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
170+
mFd = open((mProc.rtctech.cacheFolder + "/cache.lock").c_str(), O_RDWR | O_CREAT | O_CLOEXEC, mask);
171+
if (mFd == -1) {
172+
throw std::runtime_error("Error opening rtc cache mutex lock file");
173+
}
174+
fchmod(mFd, mask);
175+
if (lockf(mFd, F_LOCK, 0)) {
176+
close(mFd);
177+
mFd = -1;
178+
throw std::runtime_error("Error locking rtc cache mutex file");
179+
}
180+
}
181+
}
182+
~Cache()
183+
{
184+
if (mFd != -1) {
185+
if (lockf(mFd, F_ULOCK, 0)) {
186+
GPUError("Error unlocking RTC cache mutex file");
187+
}
188+
close(mFd);
189+
}
190+
}
191+
Cache(const Cache&) = delete;
192+
Cache& operator=(const Cache&) = delete;
193+
194+
bool load(const std::string& filename, const char* binExtension, uint32_t nCompile) // Writes the cached binaries to filename_<i><binExtension>, false if no valid cache
195+
{
196+
if (!mProc.rtc.cacheOutput) {
197+
return false;
198+
}
199+
FILE* fp = fopen(mFile.c_str(), "rb");
200+
if (fp == nullptr) {
201+
return false;
202+
}
203+
bool retVal = read(fp, filename, binExtension, nCompile);
204+
fclose(fp);
205+
if (retVal) {
206+
GPUInfo("Using RTC cache file");
207+
}
208+
return retVal;
209+
}
210+
211+
void store(const std::string& filename, const char* binExtension, uint32_t nCompile)
212+
{
213+
if (!mProc.rtc.cacheOutput) {
214+
return;
215+
}
216+
FILE* fp = fopen(mFile.c_str(), "w+b");
217+
if (fp == nullptr) {
218+
throw std::runtime_error("Cannot open cache file for writing");
219+
}
220+
GPUInfo("Storing RTC compilation result in cache file");
221+
for (uint32_t i = 0; i < N_SHA; i++) {
222+
if (fwrite(mSHA[i], 1, 20, fp) != 20) {
223+
throw std::runtime_error("Error writing cache file");
224+
}
225+
}
226+
if (fwrite(&mProc.rtc, sizeof(mProc.rtc), 1, fp) != 1) {
227+
throw std::runtime_error("Error writing cache file");
228+
}
229+
std::vector<char> buffer;
230+
for (uint32_t i = 0; i < nCompile; i++) {
231+
FILE* fp2 = fopen((filename + "_" + std::to_string(i) + binExtension).c_str(), "rb");
232+
if (fp2 == nullptr) {
233+
throw std::runtime_error("Cannot open RTC binary file");
234+
}
235+
fseek(fp2, 0, SEEK_END);
236+
size_t size = ftell(fp2);
237+
buffer.resize(size);
238+
fseek(fp2, 0, SEEK_SET);
239+
if (fread(buffer.data(), 1, size, fp2) != size) {
240+
throw std::runtime_error("Error reading RTC binary file");
241+
}
242+
fclose(fp2);
243+
if (fwrite(&size, sizeof(size), 1, fp) != 1 || fwrite(buffer.data(), 1, size, fp) != size) {
244+
throw std::runtime_error("Error writing cache file");
245+
}
246+
}
247+
fclose(fp);
248+
}
249+
250+
private:
251+
bool read(FILE* fp, const std::string& filename, const char* binExtension, uint32_t nCompile)
252+
{
253+
static constexpr const char* names[N_SHA] = {"source", "param", "command line", "kernel definitions", "launch bounds", "compiler versions"};
254+
for (uint32_t i = 0; i < N_SHA; i++) {
255+
char sharead[20];
256+
if (fread(sharead, 1, 20, fp) != 20) {
257+
throw std::runtime_error("Cache file corrupt");
258+
}
259+
if (mProc.debugLevel >= 3) {
260+
char shaprint1[41], shaprint2[41];
261+
for (uint32_t j = 0; j < 20; j++) {
262+
snprintf(shaprint1 + 2 * j, 3, "%02X", (uint8_t)mSHA[i][j]);
263+
snprintf(shaprint2 + 2 * j, 3, "%02X", (uint8_t)sharead[j]);
264+
}
265+
GPUInfo("SHA for %s: expected %s, read %s", names[i], shaprint1, shaprint2);
266+
}
267+
if (!mProc.rtctech.ignoreCacheValid && memcmp(sharead, mSHA[i], 20)) {
268+
GPUInfo("Cache file content outdated (%s)", names[i]);
269+
return false;
270+
}
271+
}
272+
GPUSettingsProcessingRTC cachedSettings;
273+
static_assert(std::is_trivially_copyable_v<GPUSettingsProcessingRTC> == true, "GPUSettingsProcessingRTC must be POD");
274+
if (fread(&cachedSettings, sizeof(cachedSettings), 1, fp) != 1) {
275+
throw std::runtime_error("Cache file corrupt");
276+
}
277+
if (!mProc.rtctech.ignoreCacheValid && !(cachedSettings == mProc.rtc)) {
278+
GPUInfo("Cache file content outdated (rtc parameters)");
279+
return false;
280+
}
281+
std::vector<char> buffer;
282+
for (uint32_t i = 0; i < nCompile; i++) {
283+
size_t len;
284+
if (fread(&len, sizeof(len), 1, fp) != 1) {
285+
throw std::runtime_error("Cache file corrupt");
286+
}
287+
buffer.resize(len);
288+
if (fread(buffer.data(), 1, len, fp) != len) {
289+
throw std::runtime_error("Cache file corrupt");
290+
}
291+
FILE* fp2 = fopen((filename + "_" + std::to_string(i) + binExtension).c_str(), "w+b");
292+
if (fp2 == nullptr) {
293+
throw std::runtime_error("Cannot open tmp file");
294+
}
295+
if (fwrite(buffer.data(), 1, len, fp2) != len) {
296+
throw std::runtime_error("Error writing file");
297+
}
298+
fclose(fp2);
299+
}
300+
return true;
301+
}
302+
303+
const GPUSettingsProcessing& mProc;
304+
const std::string mFile;
305+
char mSHA[N_SHA][21];
306+
int32_t mFd = -1;
307+
};
308+
309+
} // namespace o2::gpu::internal::rtc

0 commit comments

Comments
 (0)