Skip to content

Commit 86b0e8d

Browse files
authored
[SYCL] Enable program cache if build options are set (#3277)
- Enable in-memory program cache for programs when build options are provided. Three sources of build options are supported: - SYCL API (e.g. build options passed to build_with_kernel_type() method); - build options specifed in the program image; - SYCL_PROGRAM_COMPILE_OPTIONS, SYCL_PROGRAM_LINK_OPTIONS environment variable (overrides options from first two points. - optimize cache map keys: - add build options to program cache map keys; - remove device ID from kernel cache map keys.
1 parent 9f3a74c commit 86b0e8d

File tree

4 files changed

+71
-46
lines changed

4 files changed

+71
-46
lines changed

sycl/source/detail/kernel_program_cache.hpp

+3-4
Original file line numberDiff line numberDiff line change
@@ -67,17 +67,16 @@ class KernelProgramCache {
6767
using PiProgramT = std::remove_pointer<RT::PiProgram>::type;
6868
using PiProgramPtrT = std::atomic<PiProgramT *>;
6969
using ProgramWithBuildStateT = BuildResult<PiProgramT>;
70-
using ProgramCacheKeyT =
71-
std::pair<std::pair<SerializedObj, KernelSetId>, RT::PiDevice>;
70+
using ProgramCacheKeyT = std::pair<std::pair<SerializedObj, KernelSetId>,
71+
std::pair<RT::PiDevice, std::string>>;
7272
using ProgramCacheT = std::map<ProgramCacheKeyT, ProgramWithBuildStateT>;
7373
using ContextPtr = context_impl *;
7474

7575
using PiKernelT = std::remove_pointer<RT::PiKernel>::type;
7676

7777
using PiKernelPtrT = std::atomic<PiKernelT *>;
7878
using KernelWithBuildStateT = BuildResult<PiKernelT>;
79-
using KernelByNameT =
80-
std::map<std::pair<string_class, RT::PiDevice>, KernelWithBuildStateT>;
79+
using KernelByNameT = std::map<string_class, KernelWithBuildStateT>;
8180
using KernelCacheT = std::map<RT::PiProgram, KernelByNameT>;
8281

8382
~KernelProgramCache();

sycl/source/detail/program_impl.cpp

+7-14
Original file line numberDiff line numberDiff line change
@@ -253,20 +253,13 @@ void program_impl::build_with_kernel_name(string_class KernelName,
253253
throw_if_state_is_not(program_state::none);
254254
MProgramModuleHandle = Module;
255255
if (!is_host()) {
256-
// If there are no build options, program can be safely cached
257-
if (is_cacheable_with_options(BuildOptions)) {
258-
MProgramAndKernelCachingAllowed = true;
259-
MProgram = ProgramManager::getInstance().getBuiltPIProgram(
260-
Module, get_context(), get_devices()[0], KernelName, this,
261-
/*JITCompilationIsRequired=*/(!BuildOptions.empty()));
262-
const detail::plugin &Plugin = getPlugin();
263-
Plugin.call<PiApiKind::piProgramRetain>(MProgram);
264-
} else {
265-
create_pi_program_with_kernel_name(
266-
Module, KernelName,
267-
/*JITCompilationIsRequired=*/(!BuildOptions.empty()));
268-
build(BuildOptions);
269-
}
256+
MProgramAndKernelCachingAllowed = true;
257+
MBuildOptions = BuildOptions;
258+
MProgram = ProgramManager::getInstance().getBuiltPIProgram(
259+
Module, get_context(), get_devices()[0], KernelName, this,
260+
/*JITCompilationIsRequired=*/(!BuildOptions.empty()));
261+
const detail::plugin &Plugin = getPlugin();
262+
Plugin.call<PiApiKind::piProgramRetain>(MProgram);
270263
}
271264
MState = program_state::linked;
272265
}

sycl/source/detail/program_manager/program_manager.cpp

+34-17
Original file line numberDiff line numberDiff line change
@@ -367,11 +367,36 @@ RT::PiProgram ProgramManager::getBuiltPIProgram(OSModuleHandle M,
367367
auto GetF = [](const Locked<ProgramCacheT> &LockedCache) -> ProgramCacheT & {
368368
return LockedCache.get();
369369
};
370-
auto BuildF = [this, &M, &KSId, &Context, &Device, Prg,
371-
&JITCompilationIsRequired] {
372-
const RTDeviceBinaryImage &Img =
373-
getDeviceImage(M, KSId, Context, Device, JITCompilationIsRequired);
370+
std::string BuildOptions;
371+
if (Prg)
372+
BuildOptions = Prg->get_build_options();
373+
const RTDeviceBinaryImage &Img =
374+
getDeviceImage(M, KSId, Context, Device, JITCompilationIsRequired);
375+
std::string CompileOpts = Img.getCompileOptions();
376+
std::string LinkOpts = Img.getLinkOptions();
377+
pi_device_binary_property isEsimdImage = Img.getProperty("isEsimdImage");
378+
if (!BuildOptions.empty()) {
379+
CompileOpts += " ";
380+
CompileOpts += BuildOptions;
381+
}
382+
if (isEsimdImage && pi::DeviceBinaryProperty(isEsimdImage).asUint32()) {
383+
if (!CompileOpts.empty())
384+
CompileOpts += " ";
385+
CompileOpts += "-vc-codegen";
386+
}
374387

388+
// Build options are overridden if environment variables are present
389+
const char *CompileOptsEnv = SYCLConfig<SYCL_PROGRAM_COMPILE_OPTIONS>::get();
390+
if (CompileOptsEnv) {
391+
CompileOpts = CompileOptsEnv;
392+
}
393+
const char *LinkOptsEnv = SYCLConfig<SYCL_PROGRAM_LINK_OPTIONS>::get();
394+
if (LinkOptsEnv) {
395+
LinkOpts = LinkOptsEnv;
396+
}
397+
398+
auto BuildF = [this, &M, &KSId, &Context, &Device, Prg, &Img,
399+
&JITCompilationIsRequired, &CompileOpts, &LinkOpts] {
375400
ContextImplPtr ContextImpl = getSyclObjImpl(Context);
376401
const detail::plugin &Plugin = ContextImpl->getPlugin();
377402
RT::PiProgram NativePrg = createPIProgram(Img, Context, Device);
@@ -390,17 +415,9 @@ RT::PiProgram ProgramManager::getBuiltPIProgram(OSModuleHandle M,
390415
!SYCLConfig<SYCL_DEVICELIB_NO_FALLBACK>::get())
391416
DeviceLibReqMask = getDeviceLibReqMask(Img);
392417

393-
std::string CompileOpts = Img.getCompileOptions();
394-
pi_device_binary_property isEsimdImage = Img.getProperty("isEsimdImage");
395-
if (isEsimdImage && pi::DeviceBinaryProperty(isEsimdImage).asUint32()) {
396-
if (!CompileOpts.empty())
397-
CompileOpts += " ";
398-
CompileOpts += "-vc-codegen";
399-
}
400-
401418
ProgramPtr BuiltProgram =
402-
build(std::move(ProgramManaged), ContextImpl, CompileOpts,
403-
Img.getLinkOptions(), getRawSyclObjImpl(Device)->getHandleRef(),
419+
build(std::move(ProgramManaged), ContextImpl, CompileOpts, LinkOpts,
420+
getRawSyclObjImpl(Device)->getHandleRef(),
404421
ContextImpl->getCachedLibPrograms(), DeviceLibReqMask);
405422

406423
{
@@ -417,7 +434,8 @@ RT::PiProgram ProgramManager::getBuiltPIProgram(OSModuleHandle M,
417434
const RT::PiDevice PiDevice = getRawSyclObjImpl(Device)->getHandleRef();
418435
auto BuildResult = getOrBuild<PiProgramT, compile_program_error>(
419436
Cache,
420-
std::make_pair(std::make_pair(std::move(SpecConsts), KSId), PiDevice),
437+
std::make_pair(std::make_pair(std::move(SpecConsts), KSId),
438+
std::make_pair(PiDevice, CompileOpts + LinkOpts)),
421439
AcquireF, GetF, BuildF);
422440
return BuildResult->Ptr.load();
423441
}
@@ -465,9 +483,8 @@ std::pair<RT::PiKernel, std::mutex *> ProgramManager::getOrCreateKernel(
465483
return Result;
466484
};
467485

468-
const RT::PiDevice PiDevice = getRawSyclObjImpl(Device)->getHandleRef();
469486
auto BuildResult = getOrBuild<PiKernelT, invalid_object_error>(
470-
Cache, std::make_pair(KernelName, PiDevice), AcquireF, GetF, BuildF);
487+
Cache, KernelName, AcquireF, GetF, BuildF);
471488
return std::make_pair(BuildResult->Ptr.load(),
472489
&(BuildResult->MBuildResultMutex));
473490
}

sycl/unittests/kernel-and-program/Cache.cpp

+27-11
Original file line numberDiff line numberDiff line change
@@ -201,36 +201,51 @@ TEST_F(KernelAndProgramCacheTest, ProgramSourceNegativeCompileAndLinkWithOpts) {
201201
EXPECT_EQ(Cache.size(), 0) << "Expect empty cache for source programs";
202202
}
203203

204-
// Check that probrams built without options are cached.
204+
// Check that programs built without options are cached.
205205
TEST_F(KernelAndProgramCacheTest, ProgramBuildPositive) {
206206
if (Plt.is_host() || Plt.get_backend() != backend::opencl) {
207207
return;
208208
}
209209

210210
context Ctx{Plt};
211-
program Prg{Ctx};
211+
program Prg1{Ctx};
212+
program Prg2{Ctx};
212213

213-
Prg.build_with_kernel_type<TestKernel>();
214+
Prg1.build_with_kernel_type<TestKernel>();
215+
Prg2.build_with_kernel_type<TestKernel>();
214216
auto CtxImpl = detail::getSyclObjImpl(Ctx);
215217
detail::KernelProgramCache::ProgramCacheT &Cache =
216218
CtxImpl->getKernelProgramCache().acquireCachedPrograms().get();
217219
EXPECT_EQ(Cache.size(), 1) << "Expect non-empty cache for programs";
218220
}
219221

220-
// Check that probrams built with options are not cached.
221-
TEST_F(KernelAndProgramCacheTest, ProgramBuildNegativeBuildOpts) {
222+
// Check that programs built with options are cached.
223+
TEST_F(KernelAndProgramCacheTest, ProgramBuildPositiveBuildOpts) {
222224
if (Plt.is_host() || Plt.get_backend() != backend::opencl) {
223225
return;
224226
}
225227

226228
context Ctx{Plt};
227-
program Prg{Ctx};
229+
program Prg1{Ctx};
230+
program Prg2{Ctx};
231+
program Prg3{Ctx};
232+
program Prg4{Ctx};
233+
program Prg5{Ctx};
234+
235+
/* Build 5 instances of the same program. It is expected that there will be 3
236+
* instances of the program in the cache because Build of Prg1 is equal to
237+
* build of Prg5 and build of Prg2 is equal to build of Prg3.
238+
* */
239+
Prg1.build_with_kernel_type<TestKernel>("-a");
240+
Prg2.build_with_kernel_type<TestKernel>("-b");
241+
Prg3.build_with_kernel_type<TestKernel>("-b");
242+
Prg4.build_with_kernel_type<TestKernel>();
243+
Prg5.build_with_kernel_type<TestKernel2>("-a");
228244

229-
Prg.build_with_kernel_type<TestKernel>("-g");
230245
auto CtxImpl = detail::getSyclObjImpl(Ctx);
231246
detail::KernelProgramCache::ProgramCacheT &Cache =
232247
CtxImpl->getKernelProgramCache().acquireCachedPrograms().get();
233-
EXPECT_EQ(Cache.size(), 0) << "Expect empty cache for programs";
248+
EXPECT_EQ(Cache.size(), 3) << "Expect non-empty cache for programs";
234249
}
235250

236251
// Check that programs built with compile options are not cached.
@@ -287,8 +302,8 @@ TEST_F(KernelAndProgramCacheTest, KernelPositive) {
287302
EXPECT_EQ(Cache.size(), 1) << "Expect non-empty cache for kernels";
288303
}
289304

290-
// Check that kernels built with options are not cached.
291-
TEST_F(KernelAndProgramCacheTest, KernelNegativeBuildOpts) {
305+
// Check that kernels built with options are cached.
306+
TEST_F(KernelAndProgramCacheTest, KernelPositiveBuildOpts) {
292307
if (Plt.is_host() || Plt.get_backend() != backend::opencl) {
293308
return;
294309
}
@@ -301,10 +316,11 @@ TEST_F(KernelAndProgramCacheTest, KernelNegativeBuildOpts) {
301316
program Prg{Ctx};
302317

303318
Prg.build_with_kernel_type<TestKernel>("-g");
319+
304320
kernel Ker = Prg.get_kernel<TestKernel>();
305321
detail::KernelProgramCache::KernelCacheT &Cache =
306322
CtxImpl->getKernelProgramCache().acquireKernelsPerProgramCache().get();
307-
EXPECT_EQ(Cache.size(), 0) << "Expect empty cache for kernels";
323+
EXPECT_EQ(Cache.size(), 1) << "Expect non-empty cache for kernels";
308324
}
309325

310326
// Check that kernels built with compile options are not cached.

0 commit comments

Comments
 (0)