Skip to content

Commit 4db44a5

Browse files
committed
Raw implementation
1 parent 6854e84 commit 4db44a5

File tree

6 files changed

+267
-16
lines changed

6 files changed

+267
-16
lines changed

sycl/doc/EnvironmentVariables.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,8 @@ subject to change. Do not rely on these variables in production code.
3535
| SYCL_ENABLE_PCI | Integer | When set to 1, enables obtaining the GPU PCI address when using the Level Zero backend. The default is 0. |
3636
| SYCL_HOST_UNIFIED_MEMORY | Integer | Enforce host unified memory support or lack of it for the execution graph builder. If set to 0, it is enforced as not supported by all devices. If set to 1, it is enforced as supported by all devices. |
3737
| SYCL_CACHE_DIR | Path | Path to persistent cache root directory. Default values are `%AppData%\Intel\sycl_program_cache` for Windows and `$HOME/intel/sycl_program_cache` on Linux. |
38-
| SYCL_CACHE_ENABLED | ON, OFF | Switches persistent cache switch on or off. Default value is ON. |
39-
| SYCL_CACHE_EVICTION | ON, OFF | Switches cache eviction on or off. Default value is ON. |
38+
| SYCL_CACHE_DISABLE_PERSISTENT | Any(\*) | Switches persistent cache switch off. Default value is ON. |
39+
| SYCL_CACHE_EVICTION_DISABLE | Any(\*) | Switches cache eviction off. Default value is ON. |
4040
| SYCL_CACHE_MAX_SIZE | Positive integer | Cache eviction is triggered once total size of cached images exceeds the value in megabytes (default - 8 192 for 8 GB). Set to 0 to disable size-based cache eviction. |
4141
| SYCL_CACHE_THRESHOLD | Positive integer | Cache eviction threshold in days (default value is 7 for 1 week). Set to 0 for disabling time-based cache eviction. |
4242
| SYCL_CACHE_MIN_DEVICE_IMAGE_SIZE | Positive integer | Minimum size of device code image in kilobytes which is reasonable to cache on disk because disk access operation may take more time than do JIT compilation for it. Default value is 0 to cache all images. |

sycl/include/CL/sycl/detail/os_util.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,9 @@ class __SYCL_EXPORT OSUtil {
8080

8181
/// Deallocates the memory referenced by \p Ptr.
8282
static void alignedFree(void *Ptr);
83+
84+
/// Returns the path to directory storing on-disk SYCL program cache.
85+
static std::string getCacheRoot();
8386
};
8487

8588
} // namespace detail

sycl/source/detail/config.def

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ CONFIG(SYCL_PROGRAM_LINK_OPTIONS, 64, __SYCL_PROGRAM_LINK_OPTIONS)
2121
CONFIG(SYCL_PROGRAM_COMPILE_OPTIONS, 64, __SYCL_PROGRAM_COMPILE_OPTIONS)
2222
CONFIG(SYCL_HOST_UNIFIED_MEMORY, 1, __SYCL_HOST_UNIFIED_MEMORY)
2323
CONFIG(SYCL_CACHE_DIR, 1024, __SYCL_CACHE_DIR)
24-
CONFIG(SYCL_CACHE_ENABLED, 3, __SYCL_CACHE_ENABLED)
24+
CONFIG(SYCL_CACHE_DISABLE_PERSISTENT, 1, __SYCL_CACHE_DISABLE_PERSISTENT)
25+
CONFIG(SYCL_CACHE_EVICTION_DISABLE, 1, __SYCL_CACHE_EVICTION_DISABLE)
2526
CONFIG(SYCL_CACHE_MAX_SIZE, 16, __SYCL_CACHE_MAX_SIZE)
2627
CONFIG(SYCL_CACHE_THRESHOLD, 16, __SYCL_CACHE_THRESHOLD)
2728
CONFIG(SYCL_CACHE_MIN_DEVICE_IMAGE_SIZE, 16, __SYCL_CACHE_MIN_DEVICE_IMAGE_SIZE)

sycl/source/detail/os_util.cpp

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
#include <CL/sycl/detail/os_util.hpp>
1010
#include <CL/sycl/exception.hpp>
11+
#include <detail/config.hpp>
1112

1213
#include <cassert>
1314

@@ -121,7 +122,7 @@ std::string OSUtil::getCurrentDSODir() {
121122
//
122123
// 4) Extract an absolute path to a filename and get a dirname from it.
123124
//
124-
uintptr_t CurrentFunc = (uintptr_t) &getCurrentDSODir;
125+
uintptr_t CurrentFunc = (uintptr_t)&getCurrentDSODir;
125126
std::ifstream Stream("/proc/self/maps");
126127
Stream >> std::hex;
127128
while (!Stream.eof()) {
@@ -166,7 +167,7 @@ std::string OSUtil::getCurrentDSODir() {
166167
return "";
167168
}
168169

169-
std::string OSUtil::getDirName(const char* Path) {
170+
std::string OSUtil::getDirName(const char *Path) {
170171
std::string Tmp(Path);
171172
// dirname(3) needs a writable C string: a null-terminator is written where a
172173
// path should split.
@@ -258,6 +259,22 @@ void OSUtil::alignedFree(void *Ptr) {
258259
#endif
259260
}
260261

262+
std::string OSUtil::getCacheRoot() {
263+
static const char *PersistenCacheRoot = SYCLConfig<SYCL_CACHE_DIR>::get();
264+
if (PersistenCacheRoot)
265+
return PersistenCacheRoot;
266+
267+
#if defined(__SYCL_RT_OS_LINUX)
268+
static const char *RootDir = std::getenv("HOME");
269+
#else
270+
static const char *RootDir = std::getenv("AppData");
271+
#endif
272+
std::string Root{RootDir ? RootDir : "."};
273+
274+
Root += "/intel/sycl_cache";
275+
return Root;
276+
}
277+
261278
} // namespace detail
262279
} // namespace sycl
263280
} // __SYCL_INLINE_NAMESPACE(cl)

sycl/source/detail/program_manager/program_manager.cpp

Lines changed: 229 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -30,18 +30,26 @@
3030
#include <cstdlib>
3131
#include <cstring>
3232
#include <fstream>
33+
#include <functional>
34+
#include <libgen.h>
3335
#include <memory>
3436
#include <mutex>
3537
#include <sstream>
38+
#include <stdio.h>
39+
#include <stdlib.h>
40+
#include <string.h>
3641
#include <string>
42+
#include <sys/stat.h>
43+
#include <sys/types.h>
44+
#include <unistd.h>
3745

3846
__SYCL_INLINE_NAMESPACE(cl) {
3947
namespace sycl {
4048
namespace detail {
4149

4250
using ContextImplPtr = std::shared_ptr<cl::sycl::detail::context_impl>;
4351

44-
static constexpr int DbgProgMgr = 1;
52+
static constexpr int DbgProgMgr = 2;
4553

4654
enum BuildState { BS_InProgress, BS_Done, BS_Failed };
4755

@@ -182,6 +190,7 @@ getOrBuild(KernelProgramCache &KPCache, KeyT &&CacheKey, AcquireFT &&Acquire,
182190

183191
// only the building thread will run this
184192
try {
193+
185194
RetT *Desired = Build();
186195

187196
#ifndef NDEBUG
@@ -346,6 +355,204 @@ RT::PiProgram ProgramManager::createPIProgram(const RTDeviceBinaryImage &Img,
346355
return Res;
347356
}
348357

358+
long GetFileSize(const char *FileName) {
359+
struct stat Stat;
360+
if (!stat(FileName, &Stat))
361+
return Stat.st_size;
362+
return -1;
363+
}
364+
365+
inline bool IsFSEntryPresent(const char *Path) {
366+
struct stat Stat;
367+
return !stat(Path, &Stat);
368+
}
369+
370+
int MakePathRecur(const char *Dir, mode_t Mode) {
371+
assert((Dir != nullptr) && "Passed null-pointer as directory name.");
372+
373+
// Directory is present - do nothing
374+
if (IsFSEntryPresent(Dir))
375+
return 0;
376+
377+
char *CurDir = strdup(Dir);
378+
MakePathRecur(dirname(CurDir), Mode);
379+
if (DbgProgMgr > 1)
380+
std::cerr << "Created directory: " << CurDir << std::endl;
381+
382+
free(CurDir);
383+
return mkdir(Dir, Mode);
384+
}
385+
386+
void WriteCacheItem(const std::string &FileName,
387+
const std::vector<std::vector<char>> &Data) {
388+
std::ofstream FileStream{FileName, std::ios::binary};
389+
if (DbgProgMgr > 1) {
390+
std::cerr << "####Writing programs built for " << std::dec << Data.size()
391+
<< " devices:\n";
392+
}
393+
394+
size_t Size = Data.size();
395+
FileStream.write((char *)&Size, sizeof(Size));
396+
for (size_t i = 0; i < Data.size(); ++i) {
397+
if (DbgProgMgr > 1) {
398+
std::cerr << "\tWrite " << i << "-th image of size " << std::dec
399+
<< Data[i].size() << "\n";
400+
}
401+
Size = Data[i].size();
402+
FileStream.write((char *)&Size, sizeof(Size));
403+
FileStream.write(Data[i].data(), Size);
404+
}
405+
FileStream.close();
406+
}
407+
408+
std::vector<std::vector<char>> ReadCacheItem(const std::string &FileName) {
409+
std::vector<std::vector<char>> Res;
410+
std::ifstream FileStream{FileName, std::ios::binary};
411+
size_t ImgNum, ImgSize;
412+
FileStream.read((char *)&ImgNum, sizeof(ImgNum));
413+
if (DbgProgMgr > 1) {
414+
std::cerr << "####Reading programs built for " << std::dec << ImgNum
415+
<< " devices:\n";
416+
}
417+
418+
Res.resize(ImgNum);
419+
420+
for (size_t i = 0; i < ImgNum; ++i) {
421+
FileStream.read((char *)&ImgSize, sizeof(ImgSize));
422+
if (DbgProgMgr > 1) {
423+
std::cerr << "\tRead " << i << "-th image of size " << std::dec << ImgSize
424+
<< "\n";
425+
}
426+
427+
Res[i].resize(ImgSize);
428+
FileStream.read(Res[i].data(), ImgSize);
429+
}
430+
431+
return Res;
432+
}
433+
434+
std::string getDeviceString(const device &Device) {
435+
return {Device.get_platform().get_info<sycl::info::platform::name>() +
436+
Device.get_info<sycl::info::device::name>() +
437+
Device.get_info<sycl::info::device::version>() +
438+
Device.get_info<sycl::info::device::driver_version>()};
439+
}
440+
441+
std::string DumpBinData(const unsigned char *Data, size_t Size) {
442+
if (!Size)
443+
return "NONE";
444+
std::stringstream ss;
445+
for (size_t i = 0; i < Size; i++) {
446+
ss << std::hex << (int)Data[i];
447+
}
448+
return ss.str();
449+
}
450+
451+
std::string GetCacheItemDirName(const device &Device,
452+
const RTDeviceBinaryImage &Img,
453+
const SerializedObj SpecConsts,
454+
const std::string &BuildOptionsString) {
455+
static std::string cache_root{detail::OSUtil::getCacheRoot()};
456+
457+
std::string ImgString{
458+
DumpBinData(Img.getRawData().BinaryStart, Img.getSize())};
459+
std::string DeviceString{getDeviceString(Device)};
460+
std::string SpecConstsString{
461+
DumpBinData(SpecConsts.data(), SpecConsts.size())};
462+
std::hash<std::string> StringHasher{};
463+
return {cache_root + "/" + std::to_string(StringHasher(DeviceString)) + "/" +
464+
std::to_string(StringHasher(ImgString)) + "/" +
465+
std::to_string(StringHasher(SpecConstsString)) + "/" +
466+
std::to_string(StringHasher(BuildOptionsString))};
467+
}
468+
469+
bool IsPersistentCacheEnabled() {
470+
static const char *PersistenCacheDisabled =
471+
SYCLConfig<SYCL_CACHE_DISABLE_PERSISTENT>::get();
472+
473+
if (DbgProgMgr > 0)
474+
std::cerr << "Persistent cache "
475+
<< (PersistenCacheDisabled ? "disabled." : "enabled.")
476+
<< std::endl;
477+
return !PersistenCacheDisabled;
478+
}
479+
480+
void ProgramManager::putPIProgramToDisc(const detail::plugin &Plugin,
481+
const device &Device,
482+
const RTDeviceBinaryImage &Img,
483+
const SerializedObj SpecConsts,
484+
const std::string &BuildOptionsString,
485+
const RT::PiProgram &Program) {
486+
if (!IsPersistentCacheEnabled()) {
487+
return;
488+
}
489+
490+
static std::string DirName =
491+
GetCacheItemDirName(Device, Img, SpecConsts, BuildOptionsString);
492+
493+
size_t i = 0;
494+
std::string FileName;
495+
do {
496+
FileName = DirName + "/" + std::to_string(i++) + ".bin";
497+
} while (IsFSEntryPresent(FileName.c_str()));
498+
499+
size_t DeviceNum;
500+
Plugin.call<PiApiKind::piProgramGetInfo>(Program, PI_PROGRAM_INFO_NUM_DEVICES,
501+
sizeof(DeviceNum), &DeviceNum,
502+
nullptr);
503+
std::vector<size_t> BinarySizes(DeviceNum);
504+
Plugin.call<PiApiKind::piProgramGetInfo>(
505+
Program, PI_PROGRAM_INFO_BINARY_SIZES,
506+
sizeof(size_t) * BinarySizes.size(), BinarySizes.data(), nullptr);
507+
508+
std::vector<std::vector<char>> Result;
509+
std::vector<char *> Pointers;
510+
for (size_t I = 0; I < BinarySizes.size(); ++I) {
511+
Result.emplace_back(BinarySizes[I]);
512+
Pointers.push_back(Result[I].data());
513+
}
514+
515+
Plugin.call<PiApiKind::piProgramGetInfo>(Program, PI_PROGRAM_INFO_BINARIES,
516+
sizeof(char *) * Pointers.size(),
517+
Pointers.data(), nullptr);
518+
519+
MakePathRecur(DirName.c_str(), 0777);
520+
WriteCacheItem(FileName, Result);
521+
}
522+
523+
bool ProgramManager::getPIProgramFromDisc(ContextImplPtr ContextImpl,
524+
const device &Device,
525+
const RTDeviceBinaryImage &Img,
526+
const SerializedObj SpecConsts,
527+
const std::string &BuildOptionsString,
528+
RT::PiProgram &NativePrg) {
529+
530+
if (!IsPersistentCacheEnabled())
531+
return false;
532+
533+
std::string Path{
534+
GetCacheItemDirName(Device, Img, SpecConsts, BuildOptionsString)};
535+
536+
if (!IsFSEntryPresent(Path.c_str()))
537+
return false;
538+
539+
int i = 0;
540+
std::string BinFileName{Path + "/" + std::to_string(i) + ".bin"};
541+
while (IsFSEntryPresent(BinFileName.c_str())) {
542+
auto BinDataItem = ReadCacheItem(BinFileName);
543+
if (BinDataItem.size()) {
544+
// TODO: Build for multiple devices once supported by program manager
545+
NativePrg = createBinaryProgram(
546+
ContextImpl, Device, (const unsigned char *)BinDataItem[0].data(),
547+
BinDataItem[0].size());
548+
return true;
549+
}
550+
BinFileName = Path + "/" + std::to_string(++i) + ".bin";
551+
}
552+
553+
return false;
554+
}
555+
349556
RT::PiProgram ProgramManager::getBuiltPIProgram(OSModuleHandle M,
350557
const context &Context,
351558
const device &Device,
@@ -390,9 +597,12 @@ RT::PiProgram ProgramManager::getBuiltPIProgram(OSModuleHandle M,
390597
if (LinkOptsEnv) {
391598
LinkOpts = LinkOptsEnv;
392599
}
600+
SerializedObj SpecConsts;
601+
if (Prg)
602+
Prg->stableSerializeSpecConstRegistry(SpecConsts);
393603

394604
auto BuildF = [this, &M, &KSId, &Context, &Device, Prg, &CompileOpts,
395-
&LinkOpts, &JITCompilationIsRequired] {
605+
&LinkOpts, &JITCompilationIsRequired, SpecConsts] {
396606
const RTDeviceBinaryImage &Img =
397607
getDeviceImage(M, KSId, Context, Device, JITCompilationIsRequired);
398608
// Update only if compile options are not overwritten by environment
@@ -413,19 +623,28 @@ RT::PiProgram ProgramManager::getBuiltPIProgram(OSModuleHandle M,
413623
LinkOpts += Img.getLinkOptions();
414624
ContextImplPtr ContextImpl = getSyclObjImpl(Context);
415625
const detail::plugin &Plugin = ContextImpl->getPlugin();
416-
RT::PiProgram NativePrg = createPIProgram(Img, Context, Device);
417-
if (Prg)
418-
flushSpecConstants(*Prg, NativePrg, &Img);
626+
RT::PiProgram NativePrg;
627+
bool LoadedFromDiskCache =
628+
getPIProgramFromDisc(ContextImpl, Device, Img, SpecConsts,
629+
CompileOpts + LinkOpts, NativePrg);
630+
if (!LoadedFromDiskCache) {
631+
NativePrg = createPIProgram(Img, Context, Device);
632+
if (Prg)
633+
flushSpecConstants(*Prg, NativePrg, &Img);
634+
}
635+
419636
ProgramPtr ProgramManaged(
420637
NativePrg, Plugin.getPiPlugin().PiFunctionTable.piProgramRelease);
421638

422639
// Link a fallback implementation of device libraries if they are not
423640
// supported by a device compiler.
424-
// Pre-compiled programs are supposed to be already linked.
641+
// Pre-compiled programs (after AOT compilation or read from persitent
642+
// cache) are supposed to be already linked.
425643
// If device image is not SPIR-V, DeviceLibReqMask will be 0 which means
426644
// no fallback device library will be linked.
427645
uint32_t DeviceLibReqMask = 0;
428-
if (Img.getFormat() == PI_DEVICE_BINARY_TYPE_SPIRV &&
646+
if (!LoadedFromDiskCache &&
647+
Img.getFormat() == PI_DEVICE_BINARY_TYPE_SPIRV &&
429648
!SYCLConfig<SYCL_DEVICELIB_NO_FALLBACK>::get())
430649
DeviceLibReqMask = getDeviceLibReqMask(Img);
431650

@@ -438,13 +657,12 @@ RT::PiProgram ProgramManager::getBuiltPIProgram(OSModuleHandle M,
438657
std::lock_guard<std::mutex> Lock(MNativeProgramsMutex);
439658
NativePrograms[BuiltProgram.get()] = &Img;
440659
}
660+
if (!LoadedFromDiskCache)
661+
putPIProgramToDisc(Plugin, Device, Img, SpecConsts,
662+
CompileOpts + LinkOpts, BuiltProgram.get());
441663
return BuiltProgram.release();
442664
};
443665

444-
SerializedObj SpecConsts;
445-
if (Prg)
446-
Prg->stableSerializeSpecConstRegistry(SpecConsts);
447-
448666
const RT::PiDevice PiDevice = getRawSyclObjImpl(Device)->getHandleRef();
449667
auto BuildResult = getOrBuild<PiProgramT, compile_program_error>(
450668
Cache,

0 commit comments

Comments
 (0)