Skip to content
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
5c5cf8f
initial commit, retrieval not working yet. several todos pending
cperkinsintel Sep 18, 2024
0d3ac02
s.b. working, but seeing an error ... investigating
cperkinsintel Sep 19, 2024
e4da64d
guards
cperkinsintel Sep 24, 2024
30a11bb
zomg working
cperkinsintel Sep 25, 2024
12e12a7
code reorg, step 1
cperkinsintel Sep 26, 2024
194b518
more reorg, testing
cperkinsintel Sep 27, 2024
8844e20
last minute clean up
cperkinsintel Sep 27, 2024
f054c26
resolve merge conflicts
cperkinsintel Sep 27, 2024
4e4b93e
fix for spirv
cperkinsintel Sep 28, 2024
ae2e98a
forgot to qualify leak check for windows
cperkinsintel Sep 28, 2024
126e39a
all OCL tests should declare that ocloc dependency
cperkinsintel Sep 28, 2024
01fe513
GCC<8 support
cperkinsintel Oct 1, 2024
19c1f08
reviewer feedback
cperkinsintel Oct 1, 2024
7177dca
Merge branch 'sycl' into cperkins-kernel_compiler-sycl-cache
cperkinsintel Oct 10, 2024
e63561d
resolve merge conflict
cperkinsintel Oct 23, 2024
a571185
multiple devices supported for kernel_compiler caching
cperkinsintel Oct 23, 2024
49a3828
multiple devices
cperkinsintel Oct 25, 2024
9841c43
clang-format fighting with itself?
cperkinsintel Oct 25, 2024
31d790d
Merge branch 'sycl' into cperkins-kernel_compiler-sycl-cache
cperkinsintel Oct 25, 2024
9d646a7
resolve merge conflicts
cperkinsintel Oct 28, 2024
3340a23
finish multi-device support
cperkinsintel Oct 29, 2024
b4e8069
Merge branch 'sycl' into cperkins-kernel_compiler-sycl-cache
cperkinsintel Oct 29, 2024
e5af452
updated for new cache_trace env, added cache testing to sycl_jit
cperkinsintel Oct 29, 2024
a57c665
comments and cleanup
cperkinsintel Oct 29, 2024
5bbceb7
clang-format can go f..ree itself
cperkinsintel Oct 29, 2024
10d918b
update unsupported test
cperkinsintel Oct 30, 2024
dc7d2a7
unsupported-INTENDED
cperkinsintel Oct 30, 2024
a3d9f96
committed-tracker and other sins
cperkinsintel Nov 1, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
135 changes: 97 additions & 38 deletions sycl/source/detail/kernel_bundle_impl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include <detail/kernel_compiler/kernel_compiler_opencl.hpp>
#include <detail/kernel_compiler/kernel_compiler_sycl.hpp>
#include <detail/kernel_impl.hpp>
#include <detail/persistent_device_code_cache.hpp>
#include <detail/program_manager/program_manager.hpp>
#include <sycl/backend_types.hpp>
#include <sycl/context.hpp>
Expand Down Expand Up @@ -396,6 +397,44 @@ class kernel_bundle_impl {
return SS.str();
}

bool
extKernelCompilerFetchFromCache(const std::vector<device> Devices,
const std::vector<std::string> &BuildOptions,
const std::string &SourceStr,
ur_program_handle_t &UrProgram) {
using ContextImplPtr = std::shared_ptr<sycl::detail::context_impl>;
ContextImplPtr ContextImpl = getSyclObjImpl(MContext);
const AdapterPtr &Adapter = ContextImpl->getAdapter();

std::string UserArgs = syclex::detail::userArgsAsString(BuildOptions);
auto BinProg = PersistentDeviceCodeCache::getCompiledKernelFromDisc(
Devices[0], UserArgs, SourceStr);
if (!BinProg.empty()) {
ur_device_handle_t UrDevice = getSyclObjImpl(Devices[0])->getHandleRef();
ur_result_t BinaryStatus = UR_RESULT_SUCCESS;
ur_program_properties_t Properties = {};
std::vector<ur_program_metadata_t> Metadata = {};
Properties.stype = UR_STRUCTURE_TYPE_PROGRAM_PROPERTIES;
Properties.pNext = nullptr;
Properties.count = Metadata.size();
Properties.pMetadatas = Metadata.data();
BinaryStatus = Adapter->call_nocheck<UrApiKind::urProgramCreateWithBinary>(
ContextImpl->getHandleRef(), UrDevice, BinProg[0].size(),
(const unsigned char *)BinProg[0].data(), &Properties, &UrProgram);

if (BinaryStatus == UR_RESULT_SUCCESS) {
ur_result_t Error = Adapter->call_nocheck<UrApiKind::urProgramBuildExp>(
UrProgram,
/*num devices =*/1, &UrDevice, UserArgs.c_str());

if (Error == UR_RESULT_SUCCESS) {
return true;
}
}
}
return false;
}

std::shared_ptr<kernel_bundle_impl>
build_from_source(const std::vector<device> Devices,
const std::vector<std::string> &BuildOptions,
Expand All @@ -415,46 +454,55 @@ class kernel_bundle_impl {
DeviceVec.push_back(Dev);
}

const auto spirv = [&]() -> std::vector<uint8_t> {
if (Language == syclex::source_language::opencl) {
// if successful, the log is empty. if failed, throws an error with the
// compilation log.
const auto &SourceStr = std::get<std::string>(this->Source);
std::vector<uint32_t> IPVersionVec(Devices.size());
std::transform(DeviceVec.begin(), DeviceVec.end(), IPVersionVec.begin(),
[&](ur_device_handle_t d) {
uint32_t ipVersion = 0;
Adapter->call<UrApiKind::urDeviceGetInfo>(
d, UR_DEVICE_INFO_IP_VERSION, sizeof(uint32_t),
&ipVersion, nullptr);
return ipVersion;
});
return syclex::detail::OpenCLC_to_SPIRV(SourceStr, IPVersionVec,
BuildOptions, LogPtr);
}
if (Language == syclex::source_language::spirv) {
const auto &SourceBytes =
std::get<std::vector<std::byte>>(this->Source);
std::vector<uint8_t> Result(SourceBytes.size());
std::transform(SourceBytes.cbegin(), SourceBytes.cend(), Result.begin(),
[](std::byte B) { return static_cast<uint8_t>(B); });
return Result;
}
if (Language == syclex::source_language::sycl) {
const auto &SourceStr = std::get<std::string>(this->Source);
return syclex::detail::SYCL_to_SPIRV(SourceStr, IncludePairs,
BuildOptions, LogPtr,
RegisteredKernelNames);
}
throw sycl::exception(
make_error_code(errc::invalid),
"OpenCL C and SPIR-V are the only supported languages at this time");
}();

ur_program_handle_t UrProgram = nullptr;
// SourceStrPtr will be null when source is Spir-V bytes.
const std::string *SourceStrPtr = std::get_if<std::string>(&this->Source);
bool FetchedFromCache = false;
if (PersistentDeviceCodeCache::isEnabled() && SourceStrPtr) {
FetchedFromCache = extKernelCompilerFetchFromCache(
Devices, BuildOptions, *SourceStrPtr, UrProgram);
}

if (!FetchedFromCache) {
const auto spirv = [&]() -> std::vector<uint8_t> {
if (Language == syclex::source_language::opencl) {
// if successful, the log is empty. if failed, throws an error with
// the compilation log.
std::vector<uint32_t> IPVersionVec(Devices.size());
std::transform(DeviceVec.begin(), DeviceVec.end(), IPVersionVec.begin(),
[&](ur_device_handle_t d) {
uint32_t ipVersion = 0;
Adapter->call<UrApiKind::urDeviceGetInfo>(
d, UR_DEVICE_INFO_IP_VERSION, sizeof(uint32_t),
&ipVersion, nullptr);
return ipVersion;
});
return syclex::detail::OpenCLC_to_SPIRV(*SourceStrPtr, IPVersionVec,
BuildOptions, LogPtr);
}
if (Language == syclex::source_language::spirv) {
const auto &SourceBytes =
std::get<std::vector<std::byte>>(this->Source);
std::vector<uint8_t> Result(SourceBytes.size());
std::transform(SourceBytes.cbegin(), SourceBytes.cend(),
Result.begin(),
[](std::byte B) { return static_cast<uint8_t>(B); });
return Result;
}
if (Language == syclex::source_language::sycl) {
return syclex::detail::SYCL_to_SPIRV(*SourceStrPtr, IncludePairs,
BuildOptions, LogPtr,
RegisteredKernelNames);
}
throw sycl::exception(
make_error_code(errc::invalid),
"SYCL C++, OpenCL C and SPIR-V are the only supported "
"languages at this time");
}();

Adapter->call<UrApiKind::urProgramCreateWithIL>(ContextImpl->getHandleRef(),
spirv.data(), spirv.size(),
nullptr, &UrProgram);
spirv.data(), spirv.size(),
nullptr, &UrProgram);
// program created by urProgramCreateWithIL is implicitly retained.
if (UrProgram == nullptr)
throw sycl::exception(
Expand All @@ -470,6 +518,8 @@ class kernel_bundle_impl {
}
Adapter->checkUrResult<errc::build>(Res);

} // if(!FetchedFromCache)

// Get the number of kernels in the program.
size_t NumKernels;
Adapter->call<UrApiKind::urProgramGetInfo>(
Expand All @@ -495,6 +545,15 @@ class kernel_bundle_impl {
nullptr, MContext, MDevices, bundle_state::executable, KernelIDs,
UrProgram);
device_image_plain DevImg{DevImgImpl};

// If caching enabled and kernel not fetched from cache, cache.
if (PersistentDeviceCodeCache::isEnabled() && !FetchedFromCache &&
SourceStrPtr) {
PersistentDeviceCodeCache::putCompiledKernelToDisc(
Devices[0], syclex::detail::userArgsAsString(BuildOptions),
*SourceStrPtr, UrProgram);
}

return std::make_shared<kernel_bundle_impl>(MContext, MDevices, DevImg,
KernelNames, Language);
}
Expand Down
2 changes: 2 additions & 0 deletions sycl/source/detail/kernel_compiler/kernel_compiler_sycl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ SYCL_to_SPIRV(const std::string &Source, include_pairs_t IncludePairs,

bool SYCL_Compilation_Available();

std::string userArgsAsString(const std::vector<std::string> &UserArguments);

} // namespace detail
} // namespace ext::oneapi::experimental

Expand Down
151 changes: 123 additions & 28 deletions sycl/source/detail/persistent_device_code_cache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -110,27 +110,22 @@ getSortedImages(const std::vector<const RTDeviceBinaryImage *> &Imgs) {
return SortedImgs;
}

/* Stores built program in persistent cache
*/
void PersistentDeviceCodeCache::putItemToDisc(
const device &Device, const std::vector<const RTDeviceBinaryImage *> &Imgs,
const SerializedObj &SpecConsts, const std::string &BuildOptionsString,
const ur_program_handle_t &NativePrg) {

if (!areImagesCacheable(Imgs))
return;

std::vector<const RTDeviceBinaryImage *> SortedImgs = getSortedImages(Imgs);
std::string DirName =
getCacheItemPath(Device, SortedImgs, SpecConsts, BuildOptionsString);

if (DirName.empty())
return;
// Utility function to get a non-yet-existing unique filename.
std::string getUniqueFilename(const std::string &base_name) {
size_t i = 0;
std::string filename = base_name + "/" + std::to_string(i++);
while (OSUtil::isPathPresent(filename + ".bin") ||
OSUtil::isPathPresent(filename + ".lock")) {
filename = base_name + "/" + std::to_string(i++);
}
return filename;
}

std::vector<std::vector<char>>
getProgramBinaryData(const ur_program_handle_t &NativePrg,
const device &Device) {
auto Adapter = detail::getSyclObjImpl(Device)->getAdapter();

unsigned int DeviceNum = 0;

Adapter->call<UrApiKind::urProgramGetInfo>(
NativePrg, UR_PROGRAM_INFO_NUM_DEVICES, sizeof(DeviceNum), &DeviceNum,
nullptr);
Expand All @@ -147,22 +142,37 @@ void PersistentDeviceCodeCache::putItemToDisc(
Pointers.push_back(Result[I].data());
}

Adapter->call<UrApiKind::urProgramGetInfo>(
NativePrg, UR_PROGRAM_INFO_BINARIES, sizeof(char *) * Pointers.size(),
Pointers.data(), nullptr);
size_t i = 0;
std::string FileName;
do {
FileName = DirName + "/" + std::to_string(i++);
} while (OSUtil::isPathPresent(FileName + ".bin") ||
OSUtil::isPathPresent(FileName + ".lock"));
Adapter->call<UrApiKind::urProgramGetInfo>(NativePrg, UR_PROGRAM_INFO_BINARIES,
sizeof(char *) * Pointers.size(),
Pointers.data(), nullptr);
return Result;
}

/* Stores built program in persistent cache
*/
void PersistentDeviceCodeCache::putItemToDisc(
const device &Device, const std::vector<const RTDeviceBinaryImage *> &Imgs,
const SerializedObj &SpecConsts, const std::string &BuildOptionsString,
const ur_program_handle_t &NativePrg) {

if (!areImagesCacheable(Imgs))
return;

std::vector<const RTDeviceBinaryImage *> SortedImgs = getSortedImages(Imgs);
std::string DirName =
getCacheItemPath(Device, SortedImgs, SpecConsts, BuildOptionsString);

if (DirName.empty())
return;

try {
OSUtil::makeDir(DirName.c_str());
std::string FileName = getUniqueFilename(DirName);
LockCacheItem Lock{FileName};
if (Lock.isOwned()) {
std::string FullFileName = FileName + ".bin";
writeBinaryDataToFile(FullFileName, Result);
writeBinaryDataToFile(FullFileName,
getProgramBinaryData(NativePrg, Device));
trace("device binary has been cached: " + FullFileName);
writeSourceItem(FileName + ".src", Device, SortedImgs, SpecConsts,
BuildOptionsString);
Expand All @@ -180,6 +190,36 @@ void PersistentDeviceCodeCache::putItemToDisc(
}
}

void PersistentDeviceCodeCache::putCompiledKernelToDisc(
const device &Device, const std::string &BuildOptionsString,
const std::string &SourceStr, const ur_program_handle_t &NativePrg) {

std::string DirName =
getCompiledKernelItemPath(Device, BuildOptionsString, SourceStr);

try {
OSUtil::makeDir(DirName.c_str());
std::string FileName = getUniqueFilename(DirName);
LockCacheItem Lock{FileName};
if (Lock.isOwned()) {
std::string FullFileName = FileName + ".bin";
writeBinaryDataToFile(FullFileName,
getProgramBinaryData(NativePrg, Device));
trace("kernel_compiler binary has been cached: " + FullFileName);
} else {
PersistentDeviceCodeCache::trace("cache lock not owned " + FileName);
}
} catch (std::exception &e) {
PersistentDeviceCodeCache::trace(
std::string("exception encountered making persistent cache: ") +
e.what());
} catch (...) {
PersistentDeviceCodeCache::trace(
std::string("error outputting persistent cache: ") +
std::strerror(errno));
}
}

/* Program binaries built for one or more devices are read from persistent
* cache and returned in form of vector of programs. Each binary program is
* stored in vector of chars.
Expand Down Expand Up @@ -222,6 +262,42 @@ std::vector<std::vector<char>> PersistentDeviceCodeCache::getItemFromDisc(
return {};
}

/* kernel_compiler extension uses slightly different format for path
and does not cache a .src separate from the binary.
*/
std::vector<std::vector<char>>
PersistentDeviceCodeCache::getCompiledKernelFromDisc(
const device &Device, const std::string &BuildOptionsString,
const std::string SourceStr) {

std::string DirName =
getCompiledKernelItemPath(Device, BuildOptionsString, SourceStr);

if (DirName.empty() || !OSUtil::isPathPresent(DirName))
return {};

int i = 0;

std::string FileName{DirName + "/" + std::to_string(i)};
while (OSUtil::isPathPresent(FileName + ".bin") ||
OSUtil::isPathPresent(FileName + ".src")) {

if (!LockCacheItem::isLocked(FileName)) {
try {
std::string FullFileName = FileName + ".bin";
std::vector<std::vector<char>> res =
readBinaryDataFromFile(FullFileName);
trace("kernel_compiler using cached binary: " + FullFileName);
return res; // subject for NRVO
} catch (...) {
// If read was unsuccessfull try the next item
}
}
FileName = DirName + "/" + std::to_string(++i);
}
return {};
}

/* Returns string value which can be used to identify different device
*/
std::string PersistentDeviceCodeCache::getDeviceIDString(const device &Device) {
Expand Down Expand Up @@ -396,6 +472,25 @@ std::string PersistentDeviceCodeCache::getCacheItemPath(
std::to_string(StringHasher(BuildOptionsString));
}

std::string PersistentDeviceCodeCache::getCompiledKernelItemPath(
const device &Device, const std::string &BuildOptionsString,
const std::string SourceString) {

std::string cache_root{getRootDir()};
if (cache_root.empty()) {
trace("Disable persistent cache due to unconfigured cache root.");
return {};
}

std::string DeviceString{getDeviceIDString(Device)};
std::hash<std::string> StringHasher{};

return cache_root + "/ext_kernel_compiler" + "/" +
std::to_string(StringHasher(DeviceString)) + "/" +
std::to_string(StringHasher(BuildOptionsString)) + "/" +
std::to_string(StringHasher(SourceString));
}

/* Returns true if persistent cache is enabled.
*/
bool PersistentDeviceCodeCache::isEnabled() {
Expand Down
Loading
Loading