Skip to content

[SYCL] Implement eviction in persistent cache #16289

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 17 commits into from
Dec 20, 2024
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
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
17 changes: 17 additions & 0 deletions sycl/include/sycl/detail/os_util.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include <cstdlib> // for size_t
#include <string> // for string
#include <sys/stat.h> // for stat
#include <vector> // for vector

#ifdef _WIN32
#define __SYCL_RT_OS_WINDOWS
Expand Down Expand Up @@ -90,6 +91,22 @@ class __SYCL_EXPORT OSUtil {
}
};

// These functions are not a part of OSUtils class to prevent
// exporting them as ABI. They are only used in persistent cache
// implementation and should not be exposed to the end users.
// Get size of directory in bytes.
size_t getDirectorySize(const std::string &Path, bool ignoreError);

// Get size of file in bytes.
size_t getFileSize(const std::string &Path);

// Get list of all files in the directory along with its last modification time.
std::vector<std::pair<uint64_t, std::string>>
getFilesWithLastModificationTime(const std::string &Path, bool ignoreError);

// Function to update file modification time with current time.
void updateFileModificationTime(const std::string &Path);

} // namespace detail
} // namespace _V1
} // namespace sycl
50 changes: 50 additions & 0 deletions sycl/source/detail/config.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -806,6 +806,56 @@ template <> class SYCLConfig<SYCL_IN_MEM_CACHE_EVICTION_THRESHOLD> {
}
};

// SYCL_CACHE_MAX_SIZE accepts an integer that specifies
// the maximum size of the on-disk Program cache.
// Cache eviction is performed when the cache size exceeds the threshold.
// The thresholds are specified in bytes.
// The default value is "0" which means that eviction is disabled.
template <> class SYCLConfig<SYCL_CACHE_MAX_SIZE> {
using BaseT = SYCLConfigBase<SYCL_CACHE_MAX_SIZE>;

public:
static long long get() { return getCachedValue(); }
static void reset() { (void)getCachedValue(true); }

static long long getProgramCacheSize() { return getCachedValue(); }

static bool isPersistentCacheEvictionEnabled() {
return getProgramCacheSize() > 0;
}

private:
static long long getCachedValue(bool ResetCache = false) {
const auto Parser = []() {
const char *ValStr = BaseT::getRawValue();

// Disable eviction by default.
if (!ValStr)
return (long long)0;

long long CacheSize = 0;
try {
CacheSize = std::stoll(ValStr);
if (CacheSize < 0)
throw INVALID_CONFIG_EXCEPTION(BaseT, "Value must be non-negative");
} catch (...) {
std::string Msg =
std::string{"Invalid input to SYCL_CACHE_MAX_SIZE. Please try "
"a positive integer."};
throw exception(make_error_code(errc::runtime), Msg);
}

return CacheSize;
};

static auto EvictionThresholds = Parser();
if (ResetCache)
EvictionThresholds = Parser();

return EvictionThresholds;
}
};

#undef INVALID_CONFIG_EXCEPTION

} // namespace detail
Expand Down
116 changes: 113 additions & 3 deletions sycl/source/detail/os_util.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,21 @@
//===----------------------------------------------------------------------===//

#include <sycl/detail/os_util.hpp>
#include <sycl/exception.hpp>

#include <cassert>
#include <iostream>
#include <limits>

#if __GNUC__ && __GNUC__ < 8
// Don't include <filesystem> for GCC versions less than 8
// For GCC versions less than 8, use experimental/filesystem.
#if defined(__has_include) && __has_include(<filesystem>)
#include <filesystem>
namespace fs = std::filesystem;
#elif defined(__has_include) && __has_include(<experimental/filesystem>)
#include <experimental/filesystem>
namespace fs = std::experimental::filesystem;
#else
#include <filesystem> // C++ 17 std::create_directories
#error "OSUtils requires C++ filesystem support"
#endif

#if defined(__SYCL_RT_OS_LINUX)
Expand Down Expand Up @@ -44,6 +51,7 @@
#elif defined(__SYCL_RT_OS_DARWIN)

#include <dlfcn.h>
#include <sys/stat.h>
#include <sys/sysctl.h>
#include <sys/types.h>

Expand Down Expand Up @@ -277,6 +285,108 @@ int OSUtil::makeDir(const char *Dir) {
return 0;
}

// Get size of a directory in bytes.
size_t getDirectorySize(const std::string &Path, bool IgnoreError = false) {
size_t DirSizeVar = 0;
std::error_code EC;
for (auto It = fs::recursive_directory_iterator(Path, EC);
It != fs::recursive_directory_iterator(); It.increment(EC)) {
// Errors can happen if a file was removed/added during the iteration.
if (EC && !IgnoreError)
throw sycl::exception(make_error_code(errc::runtime),
"Failed to get directory size: " + Path + "\n" +
EC.message());

if (fs::is_regular_file(It->path()))
DirSizeVar += getFileSize(It->path().string());
}
return DirSizeVar;
}

// Get size of file in bytes.
size_t getFileSize(const std::string &Path) {
return static_cast<size_t>(fs::file_size(Path));
}

// Get list of all files in the directory along with its last modification time.
std::vector<std::pair<uint64_t, std::string>>
getFilesWithLastModificationTime(const std::string &Path,
bool IgnoreError = false) {
std::vector<std::pair<uint64_t, std::string>> Files = {};
std::error_code EC;
for (auto It = fs::recursive_directory_iterator(Path, EC);
It != fs::recursive_directory_iterator(); It.increment(EC)) {
// Errors can happen if a file was removed/added during the iteration.
if (EC && !IgnoreError)
throw sycl::exception(make_error_code(errc::runtime),
"Failed to get files with access time: " + Path +
"\n" + EC.message());

const std::string FileName = It->path().string();
if (fs::is_regular_file(It->path())) {
// For Linux and Darwin, use stats.
#if defined(__SYCL_RT_OS_LINUX) || defined(__SYCL_RT_OS_DARWIN)
struct stat StatBuf;
if (stat(FileName.c_str(), &StatBuf) == 0)
Files.push_back({StatBuf.st_atim.tv_nsec, FileName});
#elif defined(__SYCL_RT_OS_WINDOWS)
// Use GetFileAttributeExA to get file modification time.
WIN32_FILE_ATTRIBUTE_DATA FileAttr;
if (GetFileAttributesExA(FileName.c_str(), GetFileExInfoStandard,
&FileAttr)) {
ULARGE_INTEGER AccessTime;
AccessTime.HighPart = FileAttr.ftLastWriteTime.dwHighDateTime;
AccessTime.LowPart = FileAttr.ftLastWriteTime.dwLowDateTime;
Files.push_back({AccessTime.QuadPart, FileName});
} else
throw sycl::exception(make_error_code(errc::runtime),
"Failed to get file attributes for: " + FileName);
#endif // __SYCL_RT_OS
}
}

return Files;
}

// Function to update file modification time with current time.
void updateFileModificationTime(const std::string &Path) {

#if defined(__SYCL_RT_OS_WINDOWS)
// For Windows, use SetFileTime to update file access time.

// Open file with FILE_FLAG_WRITE_THROUGH and FILE_FLAG_NO_BUFFERING flags
// to ensure that the file time is updated on disk, asap.
HANDLE hFile = CreateFileA(
Path.c_str(), GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH | FILE_FLAG_NO_BUFFERING,
NULL);
if (hFile != INVALID_HANDLE_VALUE) {
FILETIME ft;
GetSystemTimeAsFileTime(&ft);
if (!SetFileTime(hFile, NULL, NULL, &ft)) {
// Print full error.
char *errorText = nullptr;
FormatMessageA(
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPSTR)&errorText, 0, NULL);

throw sycl::exception(make_error_code(errc::runtime),
"Failed to update file access time: " + Path);
}
CloseHandle(hFile);
} else {
throw sycl::exception(make_error_code(errc::runtime),
"Failed to open file: " + Path);
}

#elif defined(__SYCL_RT_OS_LINUX) || defined(__SYCL_RT_OS_DARWIN)
// For Linux and Darwin, use utimensat to update file modification time.
utimensat(0, Path.c_str(), nullptr, 0);
#endif // __SYCL_RT_OS
}

} // namespace detail
} // namespace _V1
} // namespace sycl
Loading
Loading