Skip to content

Commit 06d01a7

Browse files
committed
Store last access time of a cache entry in a file
1 parent 0ca7bef commit 06d01a7

File tree

4 files changed

+73
-157
lines changed

4 files changed

+73
-157
lines changed

sycl/include/sycl/detail/os_util.hpp

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,10 @@
1212

1313
#include <sycl/detail/export.hpp> // for __SYCL_EXPORT
1414

15-
#include <cstdlib> // for size_t
15+
#include <cstdlib> // for size_t
16+
#include <functional>
1617
#include <string> // for string
1718
#include <sys/stat.h> // for stat
18-
#include <vector> // for vector
1919

2020
#ifdef _WIN32
2121
#define __SYCL_RT_OS_WINDOWS
@@ -100,12 +100,10 @@ size_t getDirectorySize(const std::string &Path, bool ignoreError);
100100
// Get size of file in bytes.
101101
size_t getFileSize(const std::string &Path);
102102

103-
// Get list of all files in the directory along with its last modification time.
104-
std::vector<std::pair<uint64_t, std::string>>
105-
getFilesWithLastModificationTime(const std::string &Path, bool ignoreError);
106-
107-
// Function to update file modification time with current time.
108-
void updateFileModificationTime(const std::string &Path);
103+
// Function to recursively iterate over the directory and execute
104+
// 'Func' on each regular file.
105+
void fileTreeWalk(const std::string Path,
106+
std::function<void(const std::string)> Func);
109107

110108
} // namespace detail
111109
} // namespace _V1

sycl/source/detail/os_util.cpp

Lines changed: 24 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -285,106 +285,43 @@ int OSUtil::makeDir(const char *Dir) {
285285
return 0;
286286
}
287287

288-
// Get size of a directory in bytes.
289-
size_t getDirectorySize(const std::string &Path, bool IgnoreError = false) {
290-
size_t DirSizeVar = 0;
291-
std::error_code EC;
292-
for (auto It = fs::recursive_directory_iterator(Path, EC);
293-
It != fs::recursive_directory_iterator(); It.increment(EC)) {
294-
// Errors can happen if a file was removed/added during the iteration.
295-
if (EC && !IgnoreError)
296-
throw sycl::exception(make_error_code(errc::runtime),
297-
"Failed to get directory size: " + Path + "\n" +
298-
EC.message());
299-
300-
if (fs::is_regular_file(It->path()))
301-
DirSizeVar += getFileSize(It->path().string());
302-
}
303-
return DirSizeVar;
304-
}
305-
306288
// Get size of file in bytes.
307289
size_t getFileSize(const std::string &Path) {
308290
return static_cast<size_t>(fs::file_size(Path));
309291
}
310292

311-
// Get list of all files in the directory along with its last modification time.
312-
std::vector<std::pair<uint64_t, std::string>>
313-
getFilesWithLastModificationTime(const std::string &Path,
314-
bool IgnoreError = false) {
315-
std::vector<std::pair<uint64_t, std::string>> Files = {};
293+
// Function to recursively iterate over the directory and execute
294+
// 'Func' on each regular file.
295+
void fileTreeWalk(const std::string Path,
296+
std::function<void(const std::string)> Func) {
297+
316298
std::error_code EC;
317299
for (auto It = fs::recursive_directory_iterator(Path, EC);
318300
It != fs::recursive_directory_iterator(); It.increment(EC)) {
301+
319302
// Errors can happen if a file was removed/added during the iteration.
320-
if (EC && !IgnoreError)
321-
throw sycl::exception(make_error_code(errc::runtime),
322-
"Failed to get files with access time: " + Path +
323-
"\n" + EC.message());
324-
325-
const std::string FileName = It->path().string();
326-
if (fs::is_regular_file(It->path())) {
327-
// For Linux and Darwin, use stats.
328-
#if defined(__SYCL_RT_OS_LINUX) || defined(__SYCL_RT_OS_DARWIN)
329-
struct stat StatBuf;
330-
if (stat(FileName.c_str(), &StatBuf) == 0)
331-
Files.push_back({StatBuf.st_atim.tv_nsec, FileName});
332-
#elif defined(__SYCL_RT_OS_WINDOWS)
333-
// Use GetFileAttributeExA to get file modification time.
334-
WIN32_FILE_ATTRIBUTE_DATA FileAttr;
335-
if (GetFileAttributesExA(FileName.c_str(), GetFileExInfoStandard,
336-
&FileAttr)) {
337-
ULARGE_INTEGER AccessTime;
338-
AccessTime.HighPart = FileAttr.ftLastWriteTime.dwHighDateTime;
339-
AccessTime.LowPart = FileAttr.ftLastWriteTime.dwLowDateTime;
340-
Files.push_back({AccessTime.QuadPart, FileName});
341-
} else
342-
throw sycl::exception(make_error_code(errc::runtime),
343-
"Failed to get file attributes for: " + FileName);
344-
#endif // __SYCL_RT_OS
345-
}
346-
}
303+
if (EC)
304+
throw sycl::exception(
305+
make_error_code(errc::runtime),
306+
"Failed to do File Tree Walk. Ensure that the directory is not "
307+
"getting updated while FileTreeWalk is in progress.: " +
308+
Path + "\n" + EC.message());
347309

348-
return Files;
310+
if (fs::is_regular_file(It->path()))
311+
Func(It->path().string());
312+
}
349313
}
350314

351-
// Function to update file modification time with current time.
352-
void updateFileModificationTime(const std::string &Path) {
353-
354-
#if defined(__SYCL_RT_OS_WINDOWS)
355-
// For Windows, use SetFileTime to update file access time.
356-
357-
// Open file with FILE_FLAG_WRITE_THROUGH and FILE_FLAG_NO_BUFFERING flags
358-
// to ensure that the file time is updated on disk, asap.
359-
HANDLE hFile = CreateFileA(
360-
Path.c_str(), GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
361-
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH | FILE_FLAG_NO_BUFFERING,
362-
NULL);
363-
if (hFile != INVALID_HANDLE_VALUE) {
364-
FILETIME ft;
365-
GetSystemTimeAsFileTime(&ft);
366-
if (!SetFileTime(hFile, NULL, NULL, &ft)) {
367-
// Print full error.
368-
char *errorText = nullptr;
369-
FormatMessageA(
370-
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER |
371-
FORMAT_MESSAGE_IGNORE_INSERTS,
372-
NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
373-
(LPSTR)&errorText, 0, NULL);
374-
375-
throw sycl::exception(make_error_code(errc::runtime),
376-
"Failed to update file access time: " + Path);
377-
}
378-
CloseHandle(hFile);
379-
} else {
380-
throw sycl::exception(make_error_code(errc::runtime),
381-
"Failed to open file: " + Path);
382-
}
315+
// Get size of a directory in bytes.
316+
size_t getDirectorySize(const std::string &Path, bool IgnoreError = false) {
317+
size_t DirSizeVar = 0;
383318

384-
#elif defined(__SYCL_RT_OS_LINUX) || defined(__SYCL_RT_OS_DARWIN)
385-
// For Linux and Darwin, use utimensat to update file modification time.
386-
utimensat(0, Path.c_str(), nullptr, 0);
387-
#endif // __SYCL_RT_OS
319+
auto CollectFIleSize = [&DirSizeVar](const std::string Path) {
320+
DirSizeVar += getFileSize(Path);
321+
};
322+
fileTreeWalk(Path, CollectFIleSize);
323+
324+
return DirSizeVar;
388325
}
389326

390327
} // namespace detail

sycl/source/detail/persistent_device_code_cache.cpp

Lines changed: 41 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,25 @@ getProgramBinaryData(const ur_program_handle_t &NativePrg,
180180
return Result;
181181
}
182182

183+
// Save the current time in a file.
184+
void PersistentDeviceCodeCache::saveCurrentTimeInAFile(std::string FileName) {
185+
// Lock the file to prevent concurrent writes.
186+
LockCacheItem Lock{FileName};
187+
if (Lock.isOwned()) {
188+
try {
189+
std::ofstream FileStream{FileName, std::ios::trunc};
190+
FileStream << std::chrono::high_resolution_clock::now()
191+
.time_since_epoch()
192+
.count();
193+
FileStream.close();
194+
} catch (std::exception &e) {
195+
throw sycl::exception(make_error_code(errc::runtime),
196+
"Failed to save current time in a file: " +
197+
FileName + "\n" + std::string(e.what()));
198+
}
199+
}
200+
}
201+
183202
// Check if cache_size.txt file is present in the cache root directory.
184203
// If not, create it and populate it with the size of the cache directory.
185204
void PersistentDeviceCodeCache::repopulateCacheSizeFile(
@@ -245,16 +264,25 @@ void PersistentDeviceCodeCache::evictItemsFromCache(
245264
// modification time.
246265
std::vector<std::pair<uint64_t, std::string>> FilesWithAccessTime;
247266

248-
// getFileWithLastModificationTime can throw if any new file is created or
249-
// removed during the iteration. Retry in that case. When eviction is in
250-
// progress,, we don't insert any new item but processes can still read the
251-
// cache. Reading from cache can create/remove .lock file which can cause the
252-
// exception.
267+
auto CollectFileAccessTime = [&FilesWithAccessTime](const std::string File) {
268+
if (File.find("_access_time.txt") != std::string::npos) {
269+
std::ifstream FileStream{File};
270+
uint64_t AccessTime;
271+
FileStream >> AccessTime;
272+
FilesWithAccessTime.push_back({AccessTime, File});
273+
}
274+
};
275+
276+
// fileTreeWalk can throw if any new file is created or removed during the
277+
// iteration. Retry in that case. When eviction is in progress, we don't
278+
// insert any new item but processes can still read the cache. Reading from
279+
// cache can create/remove .lock file which can cause the exception.
253280
while (true) {
254281
try {
255-
FilesWithAccessTime = getFilesWithLastModificationTime(CacheRoot, false);
282+
fileTreeWalk(CacheRoot, CollectFileAccessTime);
256283
break;
257284
} catch (...) {
285+
FilesWithAccessTime.clear();
258286
// If the cache directory is removed during the iteration, retry.
259287
continue;
260288
}
@@ -272,14 +300,8 @@ void PersistentDeviceCodeCache::evictItemsFromCache(
272300
size_t CurrCacheSize = CacheSize;
273301
for (const auto &File : FilesWithAccessTime) {
274302

275-
// Remove .bin/.src/.lock extension from the file name.
276-
auto ExtLoc = File.second.find_last_of(".");
277-
const std::string FileNameWOExt = File.second.substr(0, ExtLoc);
278-
const std::string Extension = File.second.substr(ExtLoc);
279-
280-
if (Extension != ".bin")
281-
continue;
282-
303+
int pos = File.second.find("_access_time.txt");
304+
const std::string FileNameWOExt = File.second.substr(0, pos);
283305
const std::string BinFile = FileNameWOExt + ".bin";
284306
const std::string SrcFile = FileNameWOExt + ".src";
285307

@@ -404,10 +426,6 @@ void PersistentDeviceCodeCache::putItemToDisc(
404426
const SerializedObj &SpecConsts, const std::string &BuildOptionsString,
405427
const ur_program_handle_t &NativePrg) {
406428

407-
#ifdef __SYCL_INSTRUMENT_PERSISTENT_CACHE
408-
InstrumentCache Instrument{"putItemToDisc: "};
409-
#endif
410-
411429
if (!areImagesCacheable(Imgs))
412430
return;
413431

@@ -451,6 +469,8 @@ void PersistentDeviceCodeCache::putItemToDisc(
451469
// Update Total cache size after adding the new items.
452470
TotalSize += getFileSize(FileName + ".src");
453471
TotalSize += getFileSize(FileName + ".bin");
472+
473+
saveCurrentTimeInAFile(FileName + "_access_time.txt");
454474
} else {
455475
PersistentDeviceCodeCache::trace("cache lock not owned " + FileName);
456476
}
@@ -466,12 +486,8 @@ void PersistentDeviceCodeCache::putItemToDisc(
466486
}
467487

468488
// Update the cache size file and trigger cache eviction if needed.
469-
if (TotalSize) {
470-
#ifdef __SYCL_INSTRUMENT_PERSISTENT_CACHE
471-
InstrumentCache Instrument{"Eviction: "};
472-
#endif
489+
if (TotalSize)
473490
updateCacheFileSizeAndTriggerEviction(getRootDir(), TotalSize);
474-
}
475491
}
476492

477493
void PersistentDeviceCodeCache::putCompiledKernelToDisc(
@@ -523,10 +539,6 @@ std::vector<std::vector<char>> PersistentDeviceCodeCache::getItemFromDisc(
523539
if (!areImagesCacheable(Imgs))
524540
return {};
525541

526-
#ifdef __SYCL_INSTRUMENT_PERSISTENT_CACHE
527-
InstrumentCache Instrument{"getItemFromDisc: "};
528-
#endif
529-
530542
std::vector<const RTDeviceBinaryImage *> SortedImgs = getSortedImages(Imgs);
531543
std::vector<std::vector<char>> Binaries(Devices.size());
532544
std::string FileNames;
@@ -552,12 +564,8 @@ std::vector<std::vector<char>> PersistentDeviceCodeCache::getItemFromDisc(
552564

553565
// Explicitly update the access time of the file. This is required for
554566
// eviction.
555-
if (isEvictionEnabled()) {
556-
#ifdef __SYCL_INSTRUMENT_PERSISTENT_CACHE
557-
InstrumentCache Instrument{"Updating file access time: "};
558-
#endif
559-
updateFileModificationTime(FileName + ".bin");
560-
}
567+
if (isEvictionEnabled())
568+
saveCurrentTimeInAFile(FileName + "_access_time.txt");
561569

562570
FileNames += FullFileName + ";";
563571
break;

sycl/source/detail/persistent_device_code_cache.hpp

Lines changed: 2 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,6 @@
2121
#include <thread>
2222
#include <vector>
2323

24-
#define __SYCL_INSTRUMENT_PERSISTENT_CACHE
25-
26-
#ifdef __SYCL_INSTRUMENT_PERSISTENT_CACHE
27-
#include <chrono>
28-
#endif
29-
3024
namespace sycl {
3125
inline namespace _V1 {
3226
namespace detail {
@@ -97,29 +91,6 @@ class PersistentDeviceCodeCache {
9791
* - on cache read operation it is treated as cache miss.
9892
*/
9993
private:
100-
#ifdef __SYCL_INSTRUMENT_PERSISTENT_CACHE
101-
// Class to instrument cache operations.
102-
class InstrumentCache {
103-
std::string PrintMsg;
104-
std::chrono::high_resolution_clock::time_point StartTime;
105-
106-
public:
107-
InstrumentCache(const std::string &Name) : PrintMsg(Name) {
108-
// Store start time.
109-
StartTime = std::chrono::high_resolution_clock::now();
110-
}
111-
~InstrumentCache() {
112-
// Calculate time spent and print message.
113-
auto EndTime = std::chrono::high_resolution_clock::now();
114-
auto Duration = std::chrono::duration_cast<std::chrono::nanoseconds>(
115-
EndTime - StartTime)
116-
.count();
117-
PersistentDeviceCodeCache::trace(PrintMsg + std::to_string(Duration) +
118-
"ns");
119-
}
120-
};
121-
#endif
122-
12394
/* Write built binary to persistent cache
12495
* Format: BinarySize, Binary
12596
*/
@@ -259,6 +230,8 @@ class PersistentDeviceCodeCache {
259230
static void evictItemsFromCache(const std::string &CacheRoot,
260231
size_t CacheSize, size_t MaxCacheSize);
261232

233+
static void saveCurrentTimeInAFile(std::string FileName);
234+
262235
// Check if eviction is enabled.
263236
static bool isEvictionEnabled() {
264237
return SYCLConfig<SYCL_CACHE_MAX_SIZE>::isPersistentCacheEvictionEnabled();

0 commit comments

Comments
 (0)