diff --git a/sycl/include/sycl/detail/os_util.hpp b/sycl/include/sycl/detail/os_util.hpp index afc4c4b0b48c1..f5abc19597155 100644 --- a/sycl/include/sycl/detail/os_util.hpp +++ b/sycl/include/sycl/detail/os_util.hpp @@ -99,6 +99,8 @@ class __SYCL_EXPORT OSUtil { // Get list of all files in the directory along with its last access time. static std::vector> getFilesWithAccessTime(const std::string &Path); + + static size_t DirSizeVar; }; } // namespace detail diff --git a/sycl/source/detail/os_util.cpp b/sycl/source/detail/os_util.cpp index a55c3f201e32e..e6872a0025373 100644 --- a/sycl/source/detail/os_util.cpp +++ b/sycl/source/detail/os_util.cpp @@ -26,6 +26,7 @@ #include #include #include +#include // for ftw - file tree walk #include // for dirname #include #include // for PATH_MAX @@ -276,22 +277,25 @@ int OSUtil::makeDir(const char *Dir) { return 0; } +size_t OSUtil::DirSizeVar = 0; // Get size of a directory in bytes. size_t OSUtil::getDirectorySize(const std::string &Path) { - size_t Size = 0; -#if __GNUC__ && __GNUC__ < 8 - // Should we worry about this case? - assert(false && "getDirectorySize is not implemented for GCC < 8"); -#else - // Use C++17 filesystem API to get the size of the directory. - for (const auto &entry : - std::filesystem::recursive_directory_iterator(Path)) { - if (entry.is_regular_file()) - Size += entry.file_size(); - } + DirSizeVar = 0; +// Use ftw for Linux and darwin as they support posix. +#if defined(__SYCL_RT_OS_LINUX) || defined(__SYCL_RT_OS_DARWIN) + auto SumSize = + [](const char *Fpath, const struct stat *StatBuf, int TypeFlag) { + if (TypeFlag == FTW_F) + DirSizeVar += StatBuf->st_size; + return 0; + }; + + if (ftw(Path.c_str(),SumSize, 1) == -1) + std::cerr << "Failed to get directory size: " << Path << std::endl; #endif - return Size; + + return DirSizeVar; } // Get size of file in bytes. diff --git a/sycl/source/detail/persistent_device_code_cache.cpp b/sycl/source/detail/persistent_device_code_cache.cpp index d16dd0a0f92ae..9f26ca173ade0 100644 --- a/sycl/source/detail/persistent_device_code_cache.cpp +++ b/sycl/source/detail/persistent_device_code_cache.cpp @@ -182,6 +182,11 @@ getProgramBinaryData(const ur_program_handle_t &NativePrg, // If not, create it and populate it with the size of the cache directory. void PersistentDeviceCodeCache::repopulateCacheSizeFile( const std::string &CacheRoot) { + + // No need to store cache size if eviction is disabled. + if (!isEvictionEnabled()) + return; + const std::string CacheSizeFileName = "cache_size.txt"; const std::string CacheSizeFile = CacheRoot + "/" + CacheSizeFileName; @@ -193,8 +198,6 @@ void PersistentDeviceCodeCache::repopulateCacheSizeFile( // Calculate the size of the cache directory. size_t CacheSize = OSUtil::getDirectorySize(CacheRoot); - std::cerr << "Cache size: " << CacheSize << std::endl; - // Take the lock to write the cache size to the file. { LockCacheItem Lock{CacheSizeFile}; @@ -311,6 +314,10 @@ void PersistentDeviceCodeCache::evictItemsFromCache( void PersistentDeviceCodeCache::updateCacheFileSizeAndTriggerEviction( const std::string &CacheRoot, size_t ItemSize) { + // No need to store cache size if eviction is disabled. + if (!isEvictionEnabled()) + return; + const std::string CacheSizeFileName = "cache_size.txt"; const std::string CacheSizeFile = CacheRoot + "/" + CacheSizeFileName; size_t CurrentCacheSize = 0; @@ -336,7 +343,6 @@ void PersistentDeviceCodeCache::updateCacheFileSizeAndTriggerEviction( // Write the updated cache size to the file. FileStream.open(CacheSizeFile, std::ios::out | std::ios::trunc); - std::cerr << "Current cache size: " << CurrentCacheSize << std::endl; FileStream << CurrentCacheSize; FileStream.close(); break; @@ -345,9 +351,6 @@ void PersistentDeviceCodeCache::updateCacheFileSizeAndTriggerEviction( // Check if the cache size exceeds the threshold and trigger cache eviction if // needed. - if (!SYCLConfig::isPersistentCacheEvictionEnabled()) - return; - size_t MaxCacheSize = SYCLConfig::getProgramCacheSize(); if (CurrentCacheSize > MaxCacheSize) { // Trigger cache eviction. diff --git a/sycl/source/detail/persistent_device_code_cache.hpp b/sycl/source/detail/persistent_device_code_cache.hpp index ff248cac7648c..392335b9d0604 100644 --- a/sycl/source/detail/persistent_device_code_cache.hpp +++ b/sycl/source/detail/persistent_device_code_cache.hpp @@ -118,9 +118,6 @@ class PersistentDeviceCodeCache { const std::vector &SortedImgs, const SerializedObj &SpecConsts, const std::string &BuildOptionsString); - /* Returns the path to directory storing persistent device code cache.*/ - static std::string getRootDir(); - /* Form string representing device version */ static std::string getDeviceIDString(const device &Device); @@ -152,6 +149,9 @@ class PersistentDeviceCodeCache { 1024 * 1024 * 1024; public: + /* Returns the path to directory storing persistent device code cache.*/ + static std::string getRootDir(); + /* Check if on-disk cache enabled. */ static bool isEnabled(); @@ -228,6 +228,11 @@ class PersistentDeviceCodeCache { // Evict LRU items from the cache to make space for new items. static void evictItemsFromCache(const std::string &CacheRoot, size_t CacheSize, size_t MaxCacheSize); + + // Check if eviction is enabled. + static bool isEvictionEnabled() { + return SYCLConfig::isPersistentCacheEvictionEnabled(); + } }; } // namespace detail } // namespace _V1 diff --git a/sycl/unittests/kernel-and-program/PersistentDeviceCodeCache.cpp b/sycl/unittests/kernel-and-program/PersistentDeviceCodeCache.cpp index 1cd0fcee45dc7..78383d8d43562 100644 --- a/sycl/unittests/kernel-and-program/PersistentDeviceCodeCache.cpp +++ b/sycl/unittests/kernel-and-program/PersistentDeviceCodeCache.cpp @@ -135,6 +135,12 @@ class PersistentDeviceCodeCache SYCLCachePersistentChanged = true; } + // Set SYCL_CACHE_MAX_SIZE. + void SetDiskCacheEvictionEnv(const char *NewValue) { + set_env("SYCL_CACHE_MAX_SIZE", NewValue); + sycl::detail::SYCLConfig::reset(); + } + void AppendToSYCLCacheDirEnv(const char *SubDir) { std::string NewSYCLCacheDirPath{RootSYCLCacheDir}; if (NewSYCLCacheDirPath.back() != '\\' && NewSYCLCacheDirPath.back() != '/') @@ -144,6 +150,24 @@ class PersistentDeviceCodeCache sycl::detail::SYCLConfig::reset(); } + // Get the list of binary files in the cache directory. + std::vector getBinaryFileNames(std::string CachePath) { + + std::vector FileNames; + std::error_code EC; + for (llvm::sys::fs::directory_iterator DirIt(CachePath, EC); + DirIt != llvm::sys::fs::directory_iterator(); DirIt.increment(EC)) { + // Check if the file is a binary file. + std::string filename = DirIt->path(); + if (filename.find(".bin") != std::string::npos) { + // Just return the file name without the path. + FileNames.push_back(filename.substr(filename.find_last_of("/\\") + 1)); + } + } + + return FileNames; + } + void ResetSYCLCacheDirEnv() { set_env("SYCL_CACHE_DIR", RootSYCLCacheDir.c_str()); sycl::detail::SYCLConfig::reset(); @@ -169,6 +193,9 @@ class PersistentDeviceCodeCache SetSYCLCachePersistentEnv(SYCLCachePersistentBefore ? SYCLCachePersistentBefore->c_str() : nullptr); + + // Reset SYCL_CACHE_MAX_SIZE. + SetDiskCacheEvictionEnv(nullptr); ResetSYCLCacheDirEnv(); } @@ -519,6 +546,99 @@ TEST_P(PersistentDeviceCodeCache, AccessDeniedForCacheDir) { } #endif //_WIN32 +// Unit tests for testing eviction in persistent cache. +TEST_P(PersistentDeviceCodeCache, BasicEviction) { + + // Cleanup the cache directory. + std::string CacheRoot = detail::PersistentDeviceCodeCache::getRootDir(); + ASSERT_NO_ERROR(llvm::sys::fs::remove_directories(CacheRoot)); + ASSERT_NO_ERROR(llvm::sys::fs::create_directories(CacheRoot)); + + // Disable eviction for the time being. + SetDiskCacheEvictionEnv("0"); + + std::string BuildOptions{"--eviction"}; + // Put 3 items to the cache. + // Sleeping for 1 second between each put to ensure that the items are + // written to the cache with different timestamps. After that, we will + // have three binary files in the cache with different timestamps. This is + // required to keep this unit test deterministic. + detail::PersistentDeviceCodeCache::putItemToDisc({Dev}, {&Img}, {}, + BuildOptions, NativeProg); + std::this_thread::sleep_for(std::chrono::seconds(1)); + + detail::PersistentDeviceCodeCache::putItemToDisc({Dev}, {&Img}, {}, + BuildOptions, NativeProg); + std::this_thread::sleep_for(std::chrono::seconds(1)); + + detail::PersistentDeviceCodeCache::putItemToDisc({Dev}, {&Img}, {}, + BuildOptions, NativeProg); + std::this_thread::sleep_for(std::chrono::seconds(1)); + + // Retrieve 0.bin from the cache. + auto Res = detail::PersistentDeviceCodeCache::getItemFromDisc( + {Dev}, {&Img}, {}, BuildOptions); + + // Get the number of binary files in the cached item folder. + std::string ItemDir = detail::PersistentDeviceCodeCache::getCacheItemPath( + Dev, {&Img}, {}, BuildOptions); + auto BinFiles = getBinaryFileNames(ItemDir); + + EXPECT_EQ(BinFiles.size(), static_cast(3)) + << "Missing binary files. Eviction should not have happened."; + + // Get Cache size and size of each entry. Set eviction threshold so that + // just one item is evicted. + size_t CurrentCacheSize = 0; + size_t SizeOfOneEntry = + (size_t)(detail::OSUtil::getDirectorySize(CacheRoot)) + 10; + + // Set SYCL_CACHE_MAX_SIZE. + SetDiskCacheEvictionEnv(std::to_string(SizeOfOneEntry).c_str()); + + // Put 4th item to the cache. This should trigger eviction. Only the first + // item should be evicted. + detail::PersistentDeviceCodeCache::putItemToDisc({Dev}, {&Img}, {}, + BuildOptions, NativeProg); + + // We should have three binary files: 0.bin, 2.bin, 3.bin. + BinFiles = getBinaryFileNames(ItemDir); + EXPECT_EQ(BinFiles.size(), static_cast(3)) + << "Eviction failed. Wrong number of binary files in the cache."; + + // Check that 1.bin was evicted. + for (const auto &File : BinFiles) { + EXPECT_NE(File, "1.bin") + << "Eviction failed. 1.bin should have been evicted."; + } + + ASSERT_NO_ERROR(llvm::sys::fs::remove_directories(ItemDir)); +} + +// Unit test for testing size file creation and update, concurrently. +TEST_P(PersistentDeviceCodeCache, ConcurentReadWriteCacheFileSize) { + // Cleanup the cache directory. + std::string CacheRoot = detail::PersistentDeviceCodeCache::getRootDir(); + ASSERT_NO_ERROR(llvm::sys::fs::remove_directories(CacheRoot)); + ASSERT_NO_ERROR(llvm::sys::fs::create_directories(CacheRoot)); + + // Insanely large value to not trigger eviction. This test just checks + // for deadlocks/crashes when updating the size file concurrently. + SetDiskCacheEvictionEnv("10000000"); + ConcurentReadWriteCache(1, 50); +} + +// Unit test for adding and evicting cache, concurrently. +TEST_P(PersistentDeviceCodeCache, ConcurentReadWriteCacheEviction) { + // Cleanup the cache directory. + std::string CacheRoot = detail::PersistentDeviceCodeCache::getRootDir(); + ASSERT_NO_ERROR(llvm::sys::fs::remove_directories(CacheRoot)); + ASSERT_NO_ERROR(llvm::sys::fs::create_directories(CacheRoot)); + + SetDiskCacheEvictionEnv("1000"); + ConcurentReadWriteCache(2, 40); +} + INSTANTIATE_TEST_SUITE_P(PersistentDeviceCodeCacheImpl, PersistentDeviceCodeCache, ::testing::Values(SYCL_DEVICE_BINARY_TYPE_SPIRV,