Skip to content

Commit 78c8ad0

Browse files
committed
GPU: Improve RTC cache by using an LRU with 10 (by default) entries
1 parent 602edbf commit 78c8ad0

5 files changed

Lines changed: 50 additions & 8 deletions

File tree

‎GPU/GPUTracking/Base/GPUReconstructionRTCHelpers.inc‎

Lines changed: 45 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include "Framework/SHA1.h"
2323
#include "utils/timer.h"
2424

25+
#include <algorithm>
2526
#include <array>
2627
#include <cstdio>
2728
#include <cstring>
@@ -148,26 +149,40 @@ static void compile(const GPUSettingsProcessing& proc, uint32_t nCompile, const
148149
}
149150
}
150151

151-
class Cache // RTC cache file (if rtc.cacheOutput), locked while the object exists. Usage: if (!cache.load(...)) { compile; cache.store(...); }
152+
class Cache // RTC cache file <cacheFolder>/rtc.<backend>.cache.<id> (no id with rtctech.ignoreCacheValid) (if rtc.cacheOutput), id from the SHAs of the inputs, locked per backend while the object exists. Usage: if (!cache.load(...)) { compile; cache.store(...); }
152153
{
153154
public:
154155
static constexpr uint32_t N_SHA = 6;
155156
using Inputs = std::array<std::string_view, N_SHA>; // source, param, command line, kernel definitions, launch bounds, compiler versions
156157

157-
Cache(const GPUSettingsProcessing& proc, const char* cacheName, const Inputs& inputs) : mProc(proc), mFile(proc.rtctech.cacheFolder + "/" + cacheName)
158+
Cache(const GPUSettingsProcessing& proc, const char* backend, const Inputs& inputs) : mProc(proc), mPrefix(std::string("rtc.") + backend + ".cache.")
158159
{
159160
if (!mProc.rtc.cacheOutput) {
160161
return;
161162
}
163+
char shaSettings[21]; // Only for the id, the settings are compared with operator== on load (the raw bytes include padding)
162164
for (uint32_t i = 0; i < N_SHA; i++) {
163165
o2::framework::internal::SHA1(mSHA[i], inputs[i].data(), inputs[i].size());
164166
}
167+
o2::framework::internal::SHA1(shaSettings, (const char*)&mProc.rtc, sizeof(mProc.rtc));
168+
uint64_t id = 0;
169+
for (uint32_t i = 0; i <= N_SHA; i++) {
170+
for (uint32_t j = 0; j < 20; j += 5) { // XOR of all SHAs in 5-byte blocks
171+
uint64_t block = 0;
172+
memcpy(&block, (i < N_SHA ? mSHA[i] : shaSettings) + j, 5);
173+
id ^= block;
174+
}
175+
}
176+
char idStr[11];
177+
snprintf(idStr, sizeof(idStr), "%010llX", (unsigned long long)id);
178+
mFileName = mProc.rtctech.ignoreCacheValid ? mPrefix.substr(0, mPrefix.size() - 1) : (mPrefix + idStr); // Without id for ignoreCacheValid, to find the cache file for any input
179+
mFile = mProc.rtctech.cacheFolder + "/" + mFileName;
165180
if (mProc.rtctech.cacheFolder != ".") {
166181
std::filesystem::create_directories(mProc.rtctech.cacheFolder);
167182
}
168183
if (mProc.rtctech.cacheMutex) {
169184
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);
185+
mFd = open((mProc.rtctech.cacheFolder + "/cache_" + backend + ".lock").c_str(), O_RDWR | O_CREAT | O_CLOEXEC, mask);
171186
if (mFd == -1) {
172187
throw std::runtime_error("Error opening rtc cache mutex lock file");
173188
}
@@ -204,6 +219,8 @@ class Cache // RTC cache file (if rtc.cacheOutput), locked while the object exis
204219
fclose(fp);
205220
if (retVal) {
206221
GPUInfo("Using RTC cache file");
222+
std::error_code ec;
223+
std::filesystem::last_write_time(mFile, std::filesystem::file_time_type::clock::now(), ec); // Mark as recently used for removeOldFiles()
207224
}
208225
return retVal;
209226
}
@@ -245,9 +262,32 @@ class Cache // RTC cache file (if rtc.cacheOutput), locked while the object exis
245262
}
246263
}
247264
fclose(fp);
265+
removeOldFiles();
248266
}
249267

250268
private:
269+
void removeOldFiles() const // Keeps the rtctech.cacheSize most recent cache files of this backend, including the one just written
270+
{
271+
if (mProc.rtctech.cacheSize == 0 || mProc.rtctech.ignoreCacheValid) {
272+
return;
273+
}
274+
std::vector<std::pair<std::filesystem::file_time_type, std::filesystem::path>> files;
275+
std::error_code ec;
276+
for (const auto& entry : std::filesystem::directory_iterator(mProc.rtctech.cacheFolder)) {
277+
const std::string name = entry.path().filename().string();
278+
if (name.starts_with(mPrefix) && name != mFileName && entry.is_regular_file(ec)) {
279+
files.emplace_back(entry.last_write_time(ec), entry.path());
280+
}
281+
}
282+
if (files.size() < mProc.rtctech.cacheSize) {
283+
return;
284+
}
285+
std::sort(files.begin(), files.end(), std::greater<>()); // Most recent first
286+
for (uint32_t i = mProc.rtctech.cacheSize - 1; i < files.size(); i++) {
287+
std::filesystem::remove(files[i].second, ec);
288+
}
289+
}
290+
251291
bool read(FILE* fp, const std::string& filename, const char* binExtension, uint32_t nCompile)
252292
{
253293
static constexpr const char* names[N_SHA] = {"source", "param", "command line", "kernel definitions", "launch bounds", "compiler versions"};
@@ -301,7 +341,8 @@ class Cache // RTC cache file (if rtc.cacheOutput), locked while the object exis
301341
}
302342

303343
const GPUSettingsProcessing& mProc;
304-
const std::string mFile;
344+
const std::string mPrefix;
345+
std::string mFileName, mFile;
305346
char mSHA[N_SHA][21];
306347
int32_t mFd = -1;
307348
};

‎GPU/GPUTracking/Base/cpudevice/GPUReconstructionCPUDeviceGenRTC.cxx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ int32_t GPUReconstructionCPUDevice::genRTC(std::string& filename, uint32_t& nCom
6868

6969
nCompile = proc.rtc.compilePerKernel ? kernels.size() : 1;
7070
const std::string compilerVersions = getBackendVersions();
71-
rtc::Cache cache(proc, "rtc.cpudevice.cache", {rtcSource, rtcparam, baseCommand, kernelsall, launchBounds, compilerVersions});
71+
rtc::Cache cache(proc, "cpudevice", {rtcSource, rtcparam, baseCommand, kernelsall, launchBounds, compilerVersions});
7272
if (!cache.load(filename, RTC_BIN_EXTENSION, nCompile)) {
7373
rtc::compile(proc, nCompile, "CPUDevice", [&](uint32_t i) {
7474
const std::string fileBase = filename + "_" + std::to_string(i);

‎GPU/GPUTracking/Base/cuda/GPUReconstructionCUDAGenRTC.cxx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ int32_t GPUReconstructionCUDA::genRTC(std::string& filename, uint32_t& nCompile)
6363

6464
nCompile = proc.rtc.compilePerKernel ? kernels.size() : 1;
6565
const std::string compilerVersions = getBackendVersions();
66-
rtc::Cache cache(proc, "rtc.cuda.cache", {rtcSource, rtcparam, baseCommand, kernelsall, launchBounds, compilerVersions});
66+
rtc::Cache cache(proc, "cuda", {rtcSource, rtcparam, baseCommand, kernelsall, launchBounds, compilerVersions});
6767
if (!cache.load(filename, mRtcBinExtension, nCompile)) {
6868
rtc::compile(proc, nCompile, "CUDA", [&](uint32_t i) {
6969
const std::string fileBase = filename + "_" + std::to_string(i);

‎GPU/GPUTracking/Definitions/GPUSettingsList.h‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -238,10 +238,11 @@ EndConfig()
238238
BeginSubConfig(GPUSettingsProcessingRTCtechnical, rtctech, configStandalone.proc, "RTCTECH", 0, "Processing settings", proc_rtctech)
239239
AddOption(runTest, int32_t, 0, "", 0, "Do not run the actual benchmark, but just test RTC compilation (1 full test, 2 test only compilation)")
240240
AddOption(cacheMutex, bool, true, "", 0, "Use a file lock to serialize access to the cache folder")
241-
AddOption(ignoreCacheValid, bool, false, "", 0, "If set, allows to use RTC cached code files even if they are not valid for the current source code / parameters")
241+
AddOption(ignoreCacheValid, bool, false, "", 0, "If set, allows to use RTC cached code files even if they are not valid for the current source code / parameters (loads cache files without hash suffix)")
242242
AddOption(printLaunchBounds, int32_t, false, "", 0, "Print launch bounds used for RTC code as debugging option, 2 for exit after printing", def(1))
243243
AddOption(allowOptimizedSlaveReconstruction, bool, false, "", 0, "Allow RTC with slave GPUReconstruction instances with optConstexpr and optSpecialcode")
244244
AddOption(cacheFolder, std::string, "./rtccache/", "", 0, "Folder in which the cache file is stored")
245+
AddOption(cacheSize, uint32_t, 10, "", 0, "Maximum number of RTC cache files per backend in the cache folder, the oldest are removed (0 = no limit)")
245246
AddOption(prependCommand, std::string, "", "", 0, "Prepend RTC compilation commands by this string")
246247
AddOption(overrideArchitecture, std::string, "", "", 0, "Override arhcitecture part of RTC compilation command line") // Part of cmdLine, so checked against the cache
247248
AddOption(loadLaunchBoundsFromFile, std::string, "", "", 0, "Load a parameter object containing the launch bounds from a file")

‎GPU/documentation/run-time-compilation.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ Generally, RTC is enabled via the `--RTCenable` flag for the standalone benchmar
1010
For a list of RTC options, please see [GPUSettingsList.h](https://github.com/AliceO2Group/AliceO2/blob/80a80a17f5a1d9cb77743e2a39b15b653fe1a4f9/GPU/GPUTracking/Definitions/GPUSettingsList.h#L215).
1111

1212
Caching the output:
13-
- The RTC output can be cached and reused, so that when running multiple times, compilation is not repeated. This is enabled via the `--RTCcacheOutput` setting. The folder to store the cache files can be selected via `--RTCTECHcacheFolder` and with `--RTCTECHcacheMutex` (default: enabled), a file-lock mutex can be used to synchronize access to the cache folder. The cached code is checked against the to-be-compiled source code with SHA1 hashes, and only if the code is not change the cache is used, otherwise the code is recompiled and the cache updated. It is possible to force using outdated cache files via the `--RTCTECHignoreCacheValid` option.
13+
- The RTC output can be cached and reused, so that when running multiple times, compilation is not repeated. This is enabled via the `--RTCcacheOutput` setting. The folder to store the cache files can be selected via `--RTCTECHcacheFolder` and with `--RTCTECHcacheMutex` (default: enabled), a file-lock mutex (`cache_<backend>.lock`, one per backend) can be used to synchronize access to the cache folder. Each backend can have multiple cache files `rtc.<backend>.cache.<id>`, where the id is derived from the SHA1 hashes below, so that switching between configurations does not require recompilation; when a new cache file is written, only the `--RTCTECHcacheSize` (default 10, 0 = no limit) most recent files of the backend are kept. The cached code is checked against the to-be-compiled source code with SHA1 hashes, and only if the code is not change the cache is used, otherwise the code is recompiled and the cache updated. It is possible to force using outdated cache files via the `--RTCTECHignoreCacheValid` option; in this case the cache file is `rtc.<backend>.cache` without id, and old cache files are not removed.
1414

1515
For chaning the launch bounds and other parameters, please consider `--RTCTECHloadLaunchBoundsFromFile` (and `--RTCTECHprintLaunchBounds`), which can launch a parameter set which can be created via [dumpGPUDefParam.C](https://github.com/AliceO2Group/AliceO2/blob/dev/GPU/GPUTracking/Standalone/tools/dumpGPUDefParam.C). A set of default parameters is stored in `[INSTALL_FOLDER]/share/GPU`.
1616

0 commit comments

Comments
 (0)