Skip to content

Commit 1e0bd3a

Browse files
committed
Extend eviction to kernel_compiler cache
1 parent ecaab93 commit 1e0bd3a

File tree

2 files changed

+220
-0
lines changed

2 files changed

+220
-0
lines changed

sycl/source/detail/persistent_device_code_cache.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,8 @@ void PersistentDeviceCodeCache::evictItemsFromCache(
316316
auto RemoveFileAndSubtractSize = [&CurrCacheSize](
317317
const std::string &FileName) {
318318
// If the file is not present, return.
319+
// Src file is not present inj kernel_compiler cache, we will
320+
// skip removing it.
319321
if (!OSUtil::isPathPresent(FileName))
320322
return;
321323

@@ -495,7 +497,20 @@ void PersistentDeviceCodeCache::putItemToDisc(
495497
void PersistentDeviceCodeCache::putCompiledKernelToDisc(
496498
const std::vector<device> &Devices, const std::string &BuildOptionsString,
497499
const std::string &SourceStr, const ur_program_handle_t &NativePrg) {
500+
501+
repopulateCacheSizeFile(getRootDir());
502+
503+
// Do not insert any new item if eviction is in progress.
504+
// Since evictions are rare, we can afford to spin lock here.
505+
const std::string EvictionInProgressFile =
506+
getRootDir() + EvictionInProgressFileSuffix;
507+
// Stall until the other process finishes eviction.
508+
while (OSUtil::isPathPresent(EvictionInProgressFile))
509+
continue;
510+
498511
auto BinaryData = getProgramBinaryData(NativePrg, Devices);
512+
// Total size of the item that we are writing to the cache.
513+
size_t TotalSize = 0;
499514

500515
for (size_t DeviceIndex = 0; DeviceIndex < Devices.size(); DeviceIndex++) {
501516
// If we don't have binary for the device, skip it.
@@ -513,6 +528,9 @@ void PersistentDeviceCodeCache::putCompiledKernelToDisc(
513528
writeBinaryDataToFile(FullFileName, BinaryData[DeviceIndex]);
514529
PersistentDeviceCodeCache::trace_KernelCompiler(
515530
"binary has been cached: " + FullFileName);
531+
532+
TotalSize += getFileSize(FullFileName);
533+
saveCurrentTimeInAFile(FileName + CacheEntryAccessTimeSuffix);
516534
} else {
517535
PersistentDeviceCodeCache::trace_KernelCompiler(
518536
"cache lock not owned " + FileName);
@@ -525,6 +543,10 @@ void PersistentDeviceCodeCache::putCompiledKernelToDisc(
525543
std::string("error outputting cache: ") + std::strerror(errno));
526544
}
527545
}
546+
547+
// Update the cache size file and trigger cache eviction if needed.
548+
if (TotalSize)
549+
updateCacheFileSizeAndTriggerEviction(getRootDir(), TotalSize);
528550
}
529551

530552
/* Program binaries built for one or more devices are read from persistent
@@ -611,6 +633,12 @@ PersistentDeviceCodeCache::getCompiledKernelFromDisc(
611633
try {
612634
std::string FullFileName = FileName + ".bin";
613635
Binaries[DeviceIndex] = readBinaryDataFromFile(FullFileName);
636+
637+
// Explicitly update the access time of the file. This is required for
638+
// eviction.
639+
if (isEvictionEnabled())
640+
saveCurrentTimeInAFile(FileName + CacheEntryAccessTimeSuffix);
641+
614642
FileNames += FullFileName + ";";
615643
break;
616644
} catch (...) {
Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
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+
// REQUIRES: ocloc && (opencl || level_zero)
10+
// UNSUPPORTED: accelerator
11+
12+
// -- Test the kernel_compiler with OpenCL source.
13+
// RUN: %{build} -o %t.out
14+
// RUN: %{run} %t.out
15+
// RUN: %{l0_leak_check} %{run} %t.out
16+
17+
// -- Test again, with caching.
18+
// DEFINE: %{cache_vars} = env SYCL_CACHE_PERSISTENT=1 SYCL_CACHE_TRACE=5 SYCL_CACHE_DIR=%t/cache_dir
19+
// RUN: rm -rf %t/cache_dir
20+
// RUN: %{cache_vars} %t.out 2>&1 | FileCheck %s --check-prefixes=CHECK-WRITTEN-TO-CACHE
21+
// RUN: %{cache_vars} %t.out 2>&1 | FileCheck %s --check-prefixes=CHECK-READ-FROM-CACHE
22+
23+
// -- Add leak check.
24+
// RUN: rm -rf %t/cache_dir
25+
// RUN: %{l0_leak_check} %{cache_vars} %t.out 2>&1 | FileCheck %s --check-prefixes=CHECK-WRITTEN-TO-CACHE
26+
// RUN: %{l0_leak_check} %{cache_vars} %t.out 2>&1 | FileCheck %s --check-prefixes=CHECK-READ-FROM-CACHE
27+
28+
// CHECK-WRITTEN-TO-CACHE: [Persistent Cache]: enabled
29+
// CHECK-WRITTEN-TO-CACHE-NOT: [kernel_compiler Persistent Cache]: using cached binary
30+
// CHECK-WRITTEN-TO-CACHE: [kernel_compiler Persistent Cache]: binary has been cached
31+
32+
// CHECK-READ-FROM-CACHE: [Persistent Cache]: enabled
33+
// CHECK-READ-FROM-CACHE-NOT: [kernel_compiler Persistent Cache]: binary has been cached
34+
// CHECK-READ-FROM-CACHE: [kernel_compiler Persistent Cache]: using cached binary
35+
36+
#include <sycl/detail/core.hpp>
37+
#include <sycl/kernel_bundle.hpp>
38+
39+
auto constexpr CLSource = R"===(
40+
__kernel void my_kernel(__global int *in, __global int *out) {
41+
size_t i = get_global_id(0);
42+
out[i] = in[i]*2 + 100;
43+
}
44+
__kernel void her_kernel(__global int *in, __global int *out) {
45+
size_t i = get_global_id(0);
46+
out[i] = in[i]*5 + 1000;
47+
}
48+
)===";
49+
50+
auto constexpr BadCLSource = R"===(
51+
__kernel void my_kernel(__global int *in, __global int *out) {
52+
size_t i = get_global_id(0) + no semi-colon!!
53+
out[i] = in[i]*2 + 100;
54+
}
55+
)===";
56+
/*
57+
Compile Log:
58+
1:3:34: error: use of undeclared identifier 'no'
59+
size_t i = get_global_id(0) + no semi-colon!!
60+
^
61+
1:3:36: error: expected ';' at end of declaration
62+
size_t i = get_global_id(0) + no semi-colon!!
63+
^
64+
;
65+
66+
Build failed with error code: -11
67+
68+
=============
69+
70+
*/
71+
72+
using namespace sycl;
73+
74+
void testSyclKernel(sycl::queue &Q, sycl::kernel Kernel, int multiplier,
75+
int added) {
76+
constexpr int N = 4;
77+
cl_int InputArray[N] = {0, 1, 2, 3};
78+
cl_int OutputArray[N] = {};
79+
80+
sycl::buffer InputBuf(InputArray, sycl::range<1>(N));
81+
sycl::buffer OutputBuf(OutputArray, sycl::range<1>(N));
82+
83+
Q.submit([&](sycl::handler &CGH) {
84+
CGH.set_arg(0, InputBuf.get_access<sycl::access::mode::read>(CGH));
85+
CGH.set_arg(1, OutputBuf.get_access<sycl::access::mode::write>(CGH));
86+
CGH.parallel_for(sycl::range<1>{N}, Kernel);
87+
});
88+
89+
sycl::host_accessor Out{OutputBuf};
90+
for (int I = 0; I < N; I++)
91+
assert(Out[I] == ((I * multiplier) + added));
92+
}
93+
94+
void test_build_and_run() {
95+
namespace syclex = sycl::ext::oneapi::experimental;
96+
using source_kb = sycl::kernel_bundle<sycl::bundle_state::ext_oneapi_source>;
97+
using exe_kb = sycl::kernel_bundle<sycl::bundle_state::executable>;
98+
99+
// only one device is supported at this time, so we limit the queue and
100+
// context to that
101+
sycl::device d{sycl::default_selector_v};
102+
sycl::context ctx{d};
103+
sycl::queue q{ctx, d};
104+
105+
bool ok =
106+
q.get_device().ext_oneapi_can_compile(syclex::source_language::opencl);
107+
if (!ok) {
108+
std::cout << "Apparently this device does not support OpenCL C source "
109+
"kernel bundle extension: "
110+
<< q.get_device().get_info<sycl::info::device::name>()
111+
<< std::endl;
112+
return;
113+
}
114+
115+
source_kb kbSrc = syclex::create_kernel_bundle_from_source(
116+
ctx, syclex::source_language::opencl, CLSource);
117+
// compilation of empty prop list, no devices
118+
exe_kb kbExe1 = syclex::build(kbSrc);
119+
120+
// compilation with props and devices
121+
std::string log;
122+
std::vector<std::string> flags{"-cl-fast-relaxed-math",
123+
"-cl-finite-math-only"};
124+
std::vector<sycl::device> devs = kbSrc.get_devices();
125+
sycl::context ctxRes = kbSrc.get_context();
126+
assert(ctxRes == ctx);
127+
sycl::backend beRes = kbSrc.get_backend();
128+
assert(beRes == ctx.get_backend());
129+
130+
exe_kb kbExe2 = syclex::build(
131+
kbSrc, devs,
132+
syclex::properties{syclex::build_options{flags}, syclex::save_log{&log}});
133+
134+
bool hasMyKernel = kbExe2.ext_oneapi_has_kernel("my_kernel");
135+
bool hasHerKernel = kbExe2.ext_oneapi_has_kernel("her_kernel");
136+
bool notExistKernel = kbExe2.ext_oneapi_has_kernel("not_exist");
137+
assert(hasMyKernel && "my_kernel should exist, but doesn't");
138+
assert(hasHerKernel && "her_kernel should exist, but doesn't");
139+
assert(!notExistKernel && "non-existing kernel should NOT exist, but does?");
140+
141+
sycl::kernel my_kernel = kbExe2.ext_oneapi_get_kernel("my_kernel");
142+
sycl::kernel her_kernel = kbExe2.ext_oneapi_get_kernel("her_kernel");
143+
144+
auto my_num_args = my_kernel.get_info<sycl::info::kernel::num_args>();
145+
assert(my_num_args == 2 && "my_kernel should take 2 args");
146+
147+
testSyclKernel(q, my_kernel, 2, 100);
148+
testSyclKernel(q, her_kernel, 5, 1000);
149+
}
150+
151+
void test_error() {
152+
namespace syclex = sycl::ext::oneapi::experimental;
153+
using source_kb = sycl::kernel_bundle<sycl::bundle_state::ext_oneapi_source>;
154+
using exe_kb = sycl::kernel_bundle<sycl::bundle_state::executable>;
155+
156+
// only one device is supported at this time, so we limit the queue and
157+
// context to that
158+
sycl::device d{sycl::default_selector_v};
159+
sycl::context ctx{d};
160+
sycl::queue q{ctx, d};
161+
162+
bool ok =
163+
q.get_device().ext_oneapi_can_compile(syclex::source_language::opencl);
164+
if (!ok) {
165+
return;
166+
}
167+
168+
try {
169+
source_kb kbSrc = syclex::create_kernel_bundle_from_source(
170+
ctx, syclex::source_language::opencl, BadCLSource);
171+
exe_kb kbExe1 = syclex::build(kbSrc);
172+
assert(false && "we should not be here.");
173+
} catch (sycl::exception &e) {
174+
// nice!
175+
assert(e.code() == sycl::errc::build);
176+
}
177+
// any other error will escape and cause the test to fail ( as it should ).
178+
}
179+
180+
int main() {
181+
#ifndef SYCL_EXT_ONEAPI_KERNEL_COMPILER_OPENCL
182+
static_assert(false, "KernelCompiler OpenCL feature test macro undefined");
183+
#endif
184+
185+
#ifdef SYCL_EXT_ONEAPI_KERNEL_COMPILER
186+
test_build_and_run();
187+
test_error();
188+
#else
189+
static_assert(false, "Kernel Compiler feature test macro undefined");
190+
#endif
191+
return 0;
192+
}

0 commit comments

Comments
 (0)