Skip to content

Commit d482506

Browse files
[SYCL] Extend eviction to kernel_compiler cache (#16454)
#16289 implemented eviction for persistent cache. This PR extends it to `kernel_compiler` cache as well.
1 parent 7cc9e80 commit d482506

File tree

3 files changed

+193
-23
lines changed

3 files changed

+193
-23
lines changed

Diff for: sycl/source/detail/persistent_device_code_cache.cpp

+53-17
Original file line numberDiff line numberDiff line change
@@ -49,13 +49,11 @@ LockCacheItem::LockCacheItem(const std::string &Path)
4949

5050
LockCacheItem::~LockCacheItem() {
5151
if (Owned && std::remove(FileName.c_str()))
52-
PersistentDeviceCodeCache::trace("Failed to release lock file: " +
53-
FileName);
52+
PersistentDeviceCodeCache::trace("Failed to release lock file: ", FileName);
5453
}
5554

5655
// Returns true if the specified format is either SPIRV or a native binary.
57-
static bool
58-
IsSupportedImageFormat(ur::DeviceBinaryType Format) {
56+
static bool IsSupportedImageFormat(ur::DeviceBinaryType Format) {
5957
return Format == SYCL_DEVICE_BINARY_TYPE_SPIRV ||
6058
Format == SYCL_DEVICE_BINARY_TYPE_NATIVE;
6159
}
@@ -210,6 +208,16 @@ void PersistentDeviceCodeCache::repopulateCacheSizeFile(
210208
const std::string CacheSizeFileName = "cache_size.txt";
211209
const std::string CacheSizeFile = CacheRoot + "/" + CacheSizeFileName;
212210

211+
// Create cache root, if it does not exist.
212+
try {
213+
if (!OSUtil::isPathPresent(CacheRoot))
214+
OSUtil::makeDir(CacheRoot.c_str());
215+
} catch (...) {
216+
throw sycl::exception(make_error_code(errc::runtime),
217+
"Failed to create cache root directory: " +
218+
CacheRoot);
219+
}
220+
213221
// If the cache size file is not present, calculate the size of the cache size
214222
// directory and write it to the file.
215223
if (!OSUtil::isPathPresent(CacheSizeFile)) {
@@ -316,6 +324,8 @@ void PersistentDeviceCodeCache::evictItemsFromCache(
316324
auto RemoveFileAndSubtractSize = [&CurrCacheSize](
317325
const std::string &FileName) {
318326
// If the file is not present, return.
327+
// Src file is not present inj kernel_compiler cache, we will
328+
// skip removing it.
319329
if (!OSUtil::isPathPresent(FileName))
320330
return;
321331

@@ -324,7 +334,7 @@ void PersistentDeviceCodeCache::evictItemsFromCache(
324334
throw sycl::exception(make_error_code(errc::runtime),
325335
"Failed to evict cache entry: " + FileName);
326336
} else {
327-
PersistentDeviceCodeCache::trace("File removed: " + FileName);
337+
PersistentDeviceCodeCache::trace("File removed: ", FileName);
328338
CurrCacheSize -= FileSize;
329339
}
330340
};
@@ -464,7 +474,7 @@ void PersistentDeviceCodeCache::putItemToDisc(
464474
if (Lock.isOwned()) {
465475
std::string FullFileName = FileName + ".bin";
466476
writeBinaryDataToFile(FullFileName, BinaryData[DeviceIndex]);
467-
trace("device binary has been cached: " + FullFileName);
477+
trace("device binary has been cached: ", FullFileName);
468478
writeSourceItem(FileName + ".src", Devices[DeviceIndex], SortedImgs,
469479
SpecConsts, BuildOptionsString);
470480

@@ -474,7 +484,7 @@ void PersistentDeviceCodeCache::putItemToDisc(
474484

475485
saveCurrentTimeInAFile(FileName + CacheEntryAccessTimeSuffix);
476486
} else {
477-
PersistentDeviceCodeCache::trace("cache lock not owned " + FileName);
487+
PersistentDeviceCodeCache::trace("cache lock not owned ", FileName);
478488
}
479489
} catch (std::exception &e) {
480490
PersistentDeviceCodeCache::trace(
@@ -495,7 +505,20 @@ void PersistentDeviceCodeCache::putItemToDisc(
495505
void PersistentDeviceCodeCache::putCompiledKernelToDisc(
496506
const std::vector<device> &Devices, const std::string &BuildOptionsString,
497507
const std::string &SourceStr, const ur_program_handle_t &NativePrg) {
508+
509+
repopulateCacheSizeFile(getRootDir());
510+
511+
// Do not insert any new item if eviction is in progress.
512+
// Since evictions are rare, we can afford to spin lock here.
513+
const std::string EvictionInProgressFile =
514+
getRootDir() + EvictionInProgressFileSuffix;
515+
// Stall until the other process finishes eviction.
516+
while (OSUtil::isPathPresent(EvictionInProgressFile))
517+
continue;
518+
498519
auto BinaryData = getProgramBinaryData(NativePrg, Devices);
520+
// Total size of the item that we are writing to the cache.
521+
size_t TotalSize = 0;
499522

500523
for (size_t DeviceIndex = 0; DeviceIndex < Devices.size(); DeviceIndex++) {
501524
// If we don't have binary for the device, skip it.
@@ -512,10 +535,13 @@ void PersistentDeviceCodeCache::putCompiledKernelToDisc(
512535
std::string FullFileName = FileName + ".bin";
513536
writeBinaryDataToFile(FullFileName, BinaryData[DeviceIndex]);
514537
PersistentDeviceCodeCache::trace_KernelCompiler(
515-
"binary has been cached: " + FullFileName);
538+
"binary has been cached: ", FullFileName);
539+
540+
TotalSize += getFileSize(FullFileName);
541+
saveCurrentTimeInAFile(FileName + CacheEntryAccessTimeSuffix);
516542
} else {
517-
PersistentDeviceCodeCache::trace_KernelCompiler(
518-
"cache lock not owned " + FileName);
543+
PersistentDeviceCodeCache::trace_KernelCompiler("cache lock not owned ",
544+
FileName);
519545
}
520546
} catch (std::exception &e) {
521547
PersistentDeviceCodeCache::trace_KernelCompiler(
@@ -525,6 +551,10 @@ void PersistentDeviceCodeCache::putCompiledKernelToDisc(
525551
std::string("error outputting cache: ") + std::strerror(errno));
526552
}
527553
}
554+
555+
// Update the cache size file and trigger cache eviction if needed.
556+
if (TotalSize)
557+
updateCacheFileSizeAndTriggerEviction(getRootDir(), TotalSize);
528558
}
529559

530560
/* Program binaries built for one or more devices are read from persistent
@@ -581,7 +611,7 @@ std::vector<std::vector<char>> PersistentDeviceCodeCache::getItemFromDisc(
581611
if (Binaries[DeviceIndex].empty())
582612
return {};
583613
}
584-
PersistentDeviceCodeCache::trace("using cached device binary: " + FileNames);
614+
PersistentDeviceCodeCache::trace("using cached device binary: ", FileNames);
585615
return Binaries;
586616
}
587617

@@ -611,6 +641,12 @@ PersistentDeviceCodeCache::getCompiledKernelFromDisc(
611641
try {
612642
std::string FullFileName = FileName + ".bin";
613643
Binaries[DeviceIndex] = readBinaryDataFromFile(FullFileName);
644+
645+
// Explicitly update the access time of the file. This is required for
646+
// eviction.
647+
if (isEvictionEnabled())
648+
saveCurrentTimeInAFile(FileName + CacheEntryAccessTimeSuffix);
649+
614650
FileNames += FullFileName + ";";
615651
break;
616652
} catch (...) {
@@ -623,7 +659,7 @@ PersistentDeviceCodeCache::getCompiledKernelFromDisc(
623659
if (Binaries[DeviceIndex].empty())
624660
return {};
625661
}
626-
PersistentDeviceCodeCache::trace_KernelCompiler("using cached binary: " +
662+
PersistentDeviceCodeCache::trace_KernelCompiler("using cached binary: ",
627663
FileNames);
628664
return Binaries;
629665
}
@@ -654,7 +690,7 @@ void PersistentDeviceCodeCache::writeBinaryDataToFile(
654690
FileStream.write((char *)&Size, sizeof(Size));
655691
FileStream.write(Data.data(), Size);
656692
if (FileStream.fail())
657-
trace("Failed to write to binary file " + FileName);
693+
trace("Failed to write to binary file ", FileName);
658694
}
659695

660696
/* Read built binary from persistent cache. Each persistent cache file contains
@@ -671,7 +707,7 @@ PersistentDeviceCodeCache::readBinaryDataFromFile(const std::string &FileName) {
671707
size_t NumBinaries = 0;
672708
FileStream.read((char *)&NumBinaries, sizeof(NumBinaries));
673709
if (FileStream.fail()) {
674-
trace("Failed to read number of binaries from " + FileName);
710+
trace("Failed to read number of binaries from ", FileName);
675711
return {};
676712
}
677713
// Even in the old implementation we could only put a single binary to the
@@ -686,7 +722,7 @@ PersistentDeviceCodeCache::readBinaryDataFromFile(const std::string &FileName) {
686722
FileStream.close();
687723

688724
if (FileStream.fail()) {
689-
trace("Failed to read binary file from " + FileName);
725+
trace("Failed to read binary file from ", FileName);
690726
return {};
691727
}
692728

@@ -726,7 +762,7 @@ void PersistentDeviceCodeCache::writeSourceItem(
726762
FileStream.close();
727763

728764
if (FileStream.fail()) {
729-
trace("Failed to write source file to " + FileName);
765+
trace("Failed to write source file to ", FileName);
730766
}
731767
}
732768

@@ -774,7 +810,7 @@ bool PersistentDeviceCodeCache::isCacheItemSrcEqual(
774810
FileStream.close();
775811

776812
if (FileStream.fail()) {
777-
trace("Failed to read source file from " + FileName);
813+
trace("Failed to read source file from ", FileName);
778814
}
779815

780816
return true;

Diff for: sycl/source/detail/persistent_device_code_cache.hpp

+12-6
Original file line numberDiff line numberDiff line change
@@ -208,17 +208,23 @@ class PersistentDeviceCodeCache {
208208
const ur_program_handle_t &NativePrg);
209209

210210
/* Sends message to std:cerr stream when SYCL_CACHE_TRACE environemnt is set*/
211-
static void trace(const std::string &msg) {
211+
static void trace(const std::string &msg, std::string path = "") {
212212
static const bool traceEnabled =
213213
SYCLConfig<SYCL_CACHE_TRACE>::isTraceDiskCache();
214-
if (traceEnabled)
215-
std::cerr << "[Persistent Cache]: " << msg << std::endl;
214+
if (traceEnabled) {
215+
std::replace(path.begin(), path.end(), '\\', '/');
216+
std::cerr << "[Persistent Cache]: " << msg << path << std::endl;
217+
}
216218
}
217-
static void trace_KernelCompiler(const std::string &msg) {
219+
static void trace_KernelCompiler(const std::string &msg,
220+
std::string path = "") {
218221
static const bool traceEnabled =
219222
SYCLConfig<SYCL_CACHE_TRACE>::isTraceKernelCompiler();
220-
if (traceEnabled)
221-
std::cerr << "[kernel_compiler Persistent Cache]: " << msg << std::endl;
223+
if (traceEnabled) {
224+
std::replace(path.begin(), path.end(), '\\', '/');
225+
std::cerr << "[kernel_compiler Persistent Cache]: " << msg << path
226+
<< std::endl;
227+
}
222228
}
223229

224230
private:
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
//==-kernel_compiler_cache_eviction.cpp -- kernel_compiler extension tests -==//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
// Tests on-disk cache and eviction with kernel_compiler.
10+
11+
// REQUIRES: ocloc && (opencl || level_zero)
12+
// UNSUPPORTED: accelerator
13+
// UNSUPPORTED-INTENDED: kernel_compiler is not available for accelerator
14+
// devices.
15+
16+
// -- Test the kernel_compiler with OpenCL source.
17+
// RUN: %{build} -o %t.out
18+
19+
// -- Test again, with caching.
20+
// DEFINE: %{cache_vars} = env SYCL_CACHE_PERSISTENT=1 SYCL_CACHE_TRACE=7 SYCL_CACHE_DIR=%t/cache_dir SYCL_CACHE_MAX_SIZE=30000
21+
// RUN: %if run-mode %{rm -rf %t/cache_dir%}
22+
// RUN: %{cache_vars} %{run-unfiltered-devices} %t.out 2>&1 | FileCheck %s --check-prefix=CHECK
23+
24+
// CHECK: [Persistent Cache]: enabled
25+
26+
#include <sycl/detail/core.hpp>
27+
#include <sycl/kernel_bundle.hpp>
28+
29+
auto constexpr CLSource = R"===(
30+
__kernel void my_kernel(__global int *in, __global int *out) {
31+
size_t i = get_global_id(0);
32+
out[i] = in[i]*2 + 100;
33+
}
34+
__kernel void her_kernel(__global int *in, __global int *out) {
35+
size_t i = get_global_id(0);
36+
out[i] = in[i]*5 + 1000;
37+
}
38+
)===";
39+
40+
using namespace sycl;
41+
42+
void test_build_and_run() {
43+
namespace syclex = sycl::ext::oneapi::experimental;
44+
using source_kb = sycl::kernel_bundle<sycl::bundle_state::ext_oneapi_source>;
45+
46+
// only one device is supported at this time, so we limit the queue and
47+
// context to that
48+
sycl::device d{sycl::default_selector_v};
49+
sycl::context ctx{d};
50+
sycl::queue q{ctx, d};
51+
52+
bool ok =
53+
q.get_device().ext_oneapi_can_compile(syclex::source_language::opencl);
54+
if (!ok) {
55+
std::cout << "Apparently this device does not support OpenCL C source "
56+
"kernel bundle extension: "
57+
<< q.get_device().get_info<sycl::info::device::name>()
58+
<< std::endl;
59+
return;
60+
}
61+
62+
auto CreateAndVerifyKB = [](source_kb &kbSrc,
63+
std::vector<std::string> &&BuildFlags) {
64+
std::string log;
65+
std::vector<sycl::device> devs = kbSrc.get_devices();
66+
sycl::context ctxRes = kbSrc.get_context();
67+
sycl::backend beRes = kbSrc.get_backend();
68+
69+
auto kb =
70+
syclex::build(kbSrc, devs,
71+
syclex::properties{syclex::build_options{BuildFlags},
72+
syclex::save_log{&log}});
73+
74+
bool hasMyKernel = kb.ext_oneapi_has_kernel("my_kernel");
75+
bool hasHerKernel = kb.ext_oneapi_has_kernel("her_kernel");
76+
bool notExistKernel = kb.ext_oneapi_has_kernel("not_exist");
77+
assert(hasMyKernel && "my_kernel should exist, but doesn't");
78+
assert(hasHerKernel && "her_kernel should exist, but doesn't");
79+
assert(!notExistKernel && "non-existing kernel should NOT exist.");
80+
};
81+
82+
source_kb kbSrc = syclex::create_kernel_bundle_from_source(
83+
ctx, syclex::source_language::opencl, CLSource);
84+
85+
// compilation with props and devices
86+
std::vector<std::string> flags{"-cl-fast-relaxed-math",
87+
"-cl-finite-math-only", "-cl-no-signed-zeros",
88+
"-cl-unsafe-math-optimizations"};
89+
90+
// Device image #1
91+
// CHECK: [Persistent Cache]: Cache size file not present. Creating one.
92+
// CHECK-NEXT: [Persistent Cache]: Cache size file created.
93+
// CHECK-NEXT: [kernel_compiler Persistent Cache]: binary has been cached: [[DEVIMG1:.*]]
94+
// CHECK-NEXT: [Persistent Cache]: Updating the cache size file.
95+
CreateAndVerifyKB(kbSrc, {});
96+
97+
// Device image #2
98+
// CHECK-NEXT: [kernel_compiler Persistent Cache]: binary has been cached: [[DEVIMG2:.*]]
99+
// CHECK-NEXT: [Persistent Cache]: Updating the cache size file.
100+
CreateAndVerifyKB(kbSrc, {flags[0], flags[1], flags[2], flags[3]});
101+
102+
// Re-insert device image #1
103+
// CHECK-NEXT: [kernel_compiler Persistent Cache]: using cached binary: [[DEVIMG1]]
104+
CreateAndVerifyKB(kbSrc, {});
105+
106+
// Insert more unique device images to trigger cache eviction.
107+
// Make sure Device image #2 is evicted before device image #1 as
108+
// eviction is LRU-based.
109+
// CHECK: [Persistent Cache]: Cache eviction triggered.
110+
// CHECK-NEXT: [Persistent Cache]: File removed: [[DEVIMG2]]
111+
// CHECK-NEXT: [Persistent Cache]: File removed: [[DEVIMG1]]
112+
for (int i = 0; i < flags.size(); i++) {
113+
CreateAndVerifyKB(kbSrc, {flags[i]});
114+
}
115+
}
116+
117+
int main() {
118+
#ifndef SYCL_EXT_ONEAPI_KERNEL_COMPILER_OPENCL
119+
static_assert(false, "KernelCompiler OpenCL feature test macro undefined");
120+
#endif
121+
122+
#ifdef SYCL_EXT_ONEAPI_KERNEL_COMPILER
123+
test_build_and_run();
124+
#else
125+
static_assert(false, "Kernel Compiler feature test macro undefined");
126+
#endif
127+
return 0;
128+
}

0 commit comments

Comments
 (0)