Skip to content
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

Map disk cache #132

Merged
merged 63 commits into from
Mar 28, 2022
Merged
Changes from 1 commit
Commits
Show all changes
63 commits
Select commit Hold shift + click to select a range
6206368
Added a very basic disk cache to the bifrost.map() function to help s…
jaycedowell Feb 19, 2019
52ac3ed
Added basic CUDA runtime/driver version checking to the map disk cache.
jaycedowell Feb 19, 2019
3ffd92a
Small cleanup of the valitdate_cache() method of the DiskCacheMgr. U…
jaycedowell Feb 19, 2019
0cae416
Force a cleanup to map function cache when 'make' is called.
jaycedowell Feb 20, 2019
e209a80
Moved away from two files that store all of the cached kernels to a p…
jaycedowell May 16, 2019
accc1e5
Added a clear method to DiskCacheMgr in map.cpp. It's not currently …
jaycedowell May 16, 2019
6b11b8b
Added a new bfMapClearCache function to map.cpp and linked it in with…
jaycedowell May 16, 2019
b1ede2f
Updated test_map.py and test_transpose.py to always clear the map dis…
jaycedowell May 16, 2019
63b03a1
Added a flag to user.mk to disable the map disk cache at compile time.
jaycedowell Aug 30, 2019
90291f0
Fixed the dates in the various files modified.
jaycedowell Oct 2, 2019
2874bd7
Pulled in master.
jaycedowell Jul 23, 2020
f6b075e
Merge remote-tracking branch 'upstream/master' into map-disk-cache
jaycedowell Feb 13, 2021
609cf12
Merge remote-tracking branch 'upstream/master' into map-disk-cache
jaycedowell Apr 20, 2021
38071e7
Merge branch 'master' into map-disk-cache
jaycedowell Dec 3, 2021
ce0e5b5
Move the map cache directory into the user's HOME.
jaycedowell Dec 3, 2021
89a3c45
The cache is not longer in /dev/shm.
jaycedowell Dec 3, 2021
ab4ece0
Formatting cleanup.
jaycedowell Dec 3, 2021
6c7dd5f
Merge remote-tracking branch 'upstream/master' into map-disk-cache
jaycedowell Mar 22, 2022
c038eee
Formatting and cleanup.
jaycedowell Mar 22, 2022
5a0fff8
Rebuild configure.
jaycedowell Mar 22, 2022
6572c97
Rebuild configure correctly.
jaycedowell Mar 22, 2022
550f9dc
Move from CPPFLAGS to config.h for map cache control.
jaycedowell Mar 22, 2022
a87bf74
Add the map cache to the listing.
jaycedowell Mar 22, 2022
c93acbf
Rebuild configure.
jaycedowell Mar 22, 2022
6596cf9
Formatting.
jaycedowell Mar 22, 2022
d47a544
Added list_cache to bifrost.map to show the cache contents. Fixed a …
jaycedowell Mar 22, 2022
76eb87a
Clearing the cache always works if it does not exist.
jaycedowell Mar 24, 2022
f24402f
clear_cache -> clear_map_cache, plus import from __init__.py.
jaycedowell Mar 24, 2022
9408003
Clear the map cache only once per test suite.
jaycedowell Mar 24, 2022
1c8d598
list_map_cache cleanup/fixes.
jaycedowell Mar 24, 2022
6cdcbc5
Remove an old comment.
jaycedowell Mar 24, 2022
7a5d290
Remove debugging prints.
jaycedowell Mar 24, 2022
c2779e7
inline process_exists() for now.
jaycedowell Mar 24, 2022
865bef6
HAVE_MAP_CACHE follows HAVE_CUDA if the cache is requested.
jaycedowell Mar 24, 2022
4fe1d71
Simplify.
jaycedowell Mar 24, 2022
f235b21
Fix a few define problems.
jaycedowell Mar 24, 2022
7302af0
list_map_cache() formatting.
jaycedowell Mar 24, 2022
316806f
Also tag the cache with a 'map cache' version so that we can invalida…
jaycedowell Mar 24, 2022
ac1bd23
Update for the map cache.
jaycedowell Mar 24, 2022
fb30280
Make a note about using setUpClass.
jaycedowell Mar 24, 2022
e1d6b78
Small formatting cleanup.
jaycedowell Mar 24, 2022
a57921f
Style.
jaycedowell Mar 25, 2022
8851fb7
Not really a value for config.h.
jaycedowell Mar 25, 2022
3547518
More descriptive name.
jaycedowell Mar 25, 2022
1f8b80f
Match what is in #135.
jaycedowell Mar 25, 2022
50b5277
Ugh.
jaycedowell Mar 25, 2022
87ac1ab
Switch to a global control variable.
jaycedowell Mar 25, 2022
599ac73
Switch to a global control variable.
jaycedowell Mar 25, 2022
d300068
More specific.
jaycedowell Mar 25, 2022
c137507
In fileutils, catch polymorphic exceptions as references
league Mar 26, 2022
d050c34
Separate fileutils.cpp, to avoid inline duplication
league Mar 26, 2022
41f8798
If using the map cache always target the lowest arch. that the librar…
jaycedowell Mar 26, 2022
fc6ffa1
Merge branch 'map-disk-cache' of https://github.com/jaycedowell/bifro…
jaycedowell Mar 26, 2022
5724141
Added a TODO with caching options to consider.
jaycedowell Mar 26, 2022
44567fa
Expose list_map_cache
league Mar 26, 2022
3ccab0f
Constants for cache filenames; notes on fileutils system and flock.
league Mar 27, 2022
902c653
Move BF_MAP_KERNEL_CACHE_SIZE into map.h.
jaycedowell Mar 28, 2022
a65af24
Formatting.
jaycedowell Mar 28, 2022
9ddeec9
Formatting.
jaycedowell Mar 28, 2022
25c4610
Variable fix.
jaycedowell Mar 28, 2022
d75cf87
Add in a test for the cache listing function.
jaycedowell Mar 28, 2022
573cce2
Formatting.
jaycedowell Mar 28, 2022
a8f65f9
Merge branch 'master' into map-disk-cache
jaycedowell Mar 28, 2022
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
Next Next commit
Added a very basic disk cache to the bifrost.map() function to help s…
…peed things up between calls of the same code. There needs to be some more work on this to make sure the the cache is invalidated when it needs to be.
  • Loading branch information
jaycedowell committed Feb 19, 2019
commit 62063689609ef50e2da7aa784cb5bd67be6a3d6d
94 changes: 94 additions & 0 deletions src/fileutils.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/*
* Copyright (c) 2016, The Bifrost Authors. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of The Bifrost Authors nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#pragma once

#include <sys/file.h> // For flock
#include <sys/stat.h> // For fstat
#include <sys/types.h> // For getpid
#include <unistd.h> // For getpid
#include <system_error>

inline void make_dir(std::string path, int perms=775) {
if( std::system(("mkdir -p "+path+" -m "+std::to_string(perms)).c_str()) ) {
throw std::runtime_error("Failed to create path: "+path);
}
}
inline void remove_all(std::string path) {
if( std::system(("rm -rf "+path).c_str()) ) {
throw std::runtime_error("Failed to remove all: "+path);
}
}
inline void remove_dir(std::string path) {
if( std::system(("rmdir "+path+" 2> /dev/null").c_str()) ) {
throw std::runtime_error("Failed to remove dir: "+path);
}
}
inline void remove_file(std::string path) {
if( std::system(("rm -f "+path).c_str()) ) {
throw std::runtime_error("Failed to remove file: "+path);
}
}
inline bool process_exists(pid_t pid) {
struct stat s;
return !(stat(("/proc/"+std::to_string(pid)).c_str(), &s) == -1
&& errno == ENOENT);
}

inline std::string get_dirname(std::string filename) {
// TODO: This is crude, but works for our proclog use-case
return filename.substr(0, filename.find_last_of("/"));
}

class LockFile {
std::string _lockfile;
int _fd;
public:
LockFile(LockFile const& ) = delete;
LockFile& operator=(LockFile const& ) = delete;
LockFile(std::string lockfile) : _lockfile(lockfile) {
while( true ) {
_fd = open(_lockfile.c_str(), O_CREAT, 600);
flock(_fd, LOCK_EX);
struct stat fd_stat, lockfile_stat;
fstat(_fd, &fd_stat);
stat(_lockfile.c_str(), &lockfile_stat);
// Compare inodes
if( fd_stat.st_ino == lockfile_stat.st_ino ) {
// Got the lock
break;
}
close(_fd);
}
}
~LockFile() {
unlink(_lockfile.c_str());
flock(_fd, LOCK_UN);
}
};

138 changes: 136 additions & 2 deletions src/map.cpp
Original file line number Diff line number Diff line change
@@ -43,6 +43,7 @@ bfMap(3, c.shape, {"dm", "t"},
#endif

#include <bifrost/map.h>
#include "fileutils.hpp"

#include "cuda.hpp"
#include "utils.hpp"
@@ -74,6 +75,11 @@ using std::cout;
using std::cerr;
using std::endl;

#include <fstream>
using std::getline;
#include <set>
#include <mutex>

#define BF_CHECK_NVRTC(call) \
do { \
nvrtcResult ret = call; \
@@ -389,6 +395,131 @@ BFstatus build_map_kernel(int* external_ndim,
return BF_STATUS_SUCCESS;
}

class DiskCacheMgr {
static constexpr const char* _cachedir = "/dev/shm/bifrost_cache/";
std::set<std::string> _created_dirs;
mutable std::mutex _mutex;
bool _loaded;

void write_to_disk(std::string cache_key,
std::string kernel_name,
std::string ptx,
bool basic_indexing_only) {
// Do this with a file lock to avoid interference from other processes
LockFile lock(std::string(_cachedir) + ".lock");

std::ofstream index, cache;
try {
// Open
cache.open(std::string(_cachedir)+"map_cache.bf", std::ios::app);
index.open(std::string(_cachedir)+"map_index.bf", std::ios::app);

// Write
cache.write(ptx.c_str(), ptx.size());
index << ptx.size() << "~" << kernel_name << "~" << basic_indexing_only << "~" << cache_key << "~" << std::endl;

// Done
cache.close();
index.close();
//cout << "SAVED TO DISK " << cache_key << endl;
} catch( std::exception ) {}
}

void load_from_disk(ObjectCache<std::string,std::pair<CUDAKernel,bool> > *kernel_cache) {
std::ifstream index, cache;
size_t kernel_size;
std::string kernel_name;
bool basic_indexing_only;
std::string cache_key;
std::string ptx;
CUDAKernel kernel;

std::string field;
std::vector<std::string> separated_fields;

// Do this with a file lock to avoid interference from other processes
LockFile lock(std::string(_cachedir) + ".lock");

try {
// Open
index.open(std::string(_cachedir)+"map_index.bf", std::ios::in);
cache.open(std::string(_cachedir)+"map_cache.bf", std::ios::in);

// Read in fields separated by '~' until we get four, and then build the kernels
while( getline(index, field, '~') ) {
separated_fields.push_back(field);
if( separated_fields.size() == 4 ) {
// PTX size
kernel_size = std::atoi(separated_fields[0].c_str());
// Kernel name
kernel_name = separated_fields[1];
// Whether or not the kernel supports basic indexing only
basic_indexing_only = false;
if( std::atoi(separated_fields[2].c_str()) > 0 ) {
basic_indexing_only = true;
}
// In-memory cache name
cache_key = separated_fields[3];

// Check the cache to see if it is aleady there...
if( !kernel_cache->contains(cache_key) ) {
ptx.resize(kernel_size);
cache.read(&ptx[0], kernel_size);
kernel.set(kernel_name.c_str(), ptx.c_str());

kernel_cache->insert(cache_key,
std::make_pair(kernel, basic_indexing_only));
//cout << "LOADED FROM DISK " << cache_key << endl;
}

separated_fields.clear();
}
}

// Done
index.close();
cache.close();
} catch( std::exception ) {}
}

DiskCacheMgr()
: _loaded(false) {
make_dir(_cachedir);
}

public:
DiskCacheMgr(DiskCacheMgr& ) = delete;
DiskCacheMgr& operator=(DiskCacheMgr& ) = delete;

static DiskCacheMgr& get() {
static DiskCacheMgr cache;
return cache;
}

bool load(ObjectCache<std::string,std::pair<CUDAKernel,bool> > *kernel_cache) {
if( !_loaded ) {
std::lock_guard<std::mutex> lock(_mutex);
this->load_from_disk(kernel_cache);
_loaded = true;
}
return _loaded;
}

void save(std::string cache_key,
std::string kernel_name,
std::string ptx,
bool basic_indexing_only) {
std::lock_guard<std::mutex> lock(_mutex);
this->write_to_disk(cache_key, kernel_name, ptx, basic_indexing_only);
}

void flush(void) {
std::lock_guard<std::mutex> lock(_mutex);
remove_file(std::string(_cachedir)+"map_cache.bf");
remove_file(std::string(_cachedir)+"map_index.bf");
}
};

BFstatus bfMap(int ndim,
long const* shape,
char const*const* axis_names,
@@ -403,7 +534,7 @@ BFstatus bfMap(int ndim,
// Map containing compiled kernels and basic_indexing_only flag
thread_local static ObjectCache<std::string,std::pair<CUDAKernel,bool> >
kernel_cache(BF_MAP_KERNEL_CACHE_SIZE);
BF_ASSERT(ndim >= 0, BF_STATUS_INVALID_ARGUMENT);
BF_ASSERT(ndim >= 0, BF_STATUS_INVALID_ARGUMENT);
//BF_ASSERT(!ndim || shape, BF_STATUS_INVALID_POINTER);
//BF_ASSERT(!ndim || axis_names, BF_STATUS_INVALID_POINTER);
BF_ASSERT(narg >= 0, BF_STATUS_INVALID_ARGUMENT);
@@ -462,6 +593,8 @@ BFstatus bfMap(int ndim,
}
std::string cache_key = cache_key_ss.str();

DiskCacheMgr::get().load(&kernel_cache);

if( !kernel_cache.contains(cache_key) ) {
std::string ptx;
std::string kernel_name;
@@ -485,10 +618,11 @@ BFstatus bfMap(int ndim,
basic_indexing_only,
&ptx, &kernel_name));
}
CUDAKernel kernel;
CUDAKernel kernel;
BF_TRY(kernel.set(kernel_name.c_str(), ptx.c_str()));
kernel_cache.insert(cache_key,
std::make_pair(kernel, basic_indexing_only));
DiskCacheMgr::get().save(cache_key, kernel_name, ptx, basic_indexing_only);
//std::cout << "INSERTING INTO CACHE" << std::endl;
} else {
//std::cout << "FOUND IN CACHE" << std::endl;
61 changes: 1 addition & 60 deletions src/proclog.cpp
Original file line number Diff line number Diff line change
@@ -29,77 +29,18 @@
#include <bifrost/proclog.h>
#include "trace.hpp"
#include "proclog.hpp"
#include "fileutils.hpp"

#include <fstream>
#include <cstdlib> // For system
#include <cstdarg> // For va_start, va_list, va_end
#include <sys/file.h> // For flock
#include <sys/stat.h> // For fstat
#include <sys/types.h> // For getpid
#include <dirent.h> // For opendir, readdir, closedir
#include <unistd.h> // For getpid
#include <system_error>
#include <set>
#include <mutex>

void make_dir(std::string path, int perms=775) {
if( std::system(("mkdir -p "+path+" -m "+std::to_string(perms)).c_str()) ) {
throw std::runtime_error("Failed to create path: "+path);
}
}
void remove_all(std::string path) {
if( std::system(("rm -rf "+path).c_str()) ) {
throw std::runtime_error("Failed to remove all: "+path);
}
}
void remove_dir(std::string path) {
if( std::system(("rmdir "+path+" 2> /dev/null").c_str()) ) {
throw std::runtime_error("Failed to remove dir: "+path);
}
}
void remove_file(std::string path) {
if( std::system(("rm -f "+path).c_str()) ) {
throw std::runtime_error("Failed to remove file: "+path);
}
}
bool process_exists(pid_t pid) {
struct stat s;
return !(stat(("/proc/"+std::to_string(pid)).c_str(), &s) == -1
&& errno == ENOENT);
}

std::string get_dirname(std::string filename) {
// TODO: This is crude, but works for our proclog use-case
return filename.substr(0, filename.find_last_of("/"));
}

class LockFile {
std::string _lockfile;
int _fd;
public:
LockFile(LockFile const& ) = delete;
LockFile& operator=(LockFile const& ) = delete;
LockFile(std::string lockfile) : _lockfile(lockfile) {
while( true ) {
_fd = open(_lockfile.c_str(), O_CREAT, 600);
flock(_fd, LOCK_EX);
struct stat fd_stat, lockfile_stat;
fstat(_fd, &fd_stat);
stat(_lockfile.c_str(), &lockfile_stat);
// Compare inodes
if( fd_stat.st_ino == lockfile_stat.st_ino ) {
// Got the lock
break;
}
close(_fd);
}
}
~LockFile() {
unlink(_lockfile.c_str());
flock(_fd, LOCK_UN);
}
};

class ProcLogMgr {
static constexpr const char* base_logdir = "/dev/shm/bifrost";
std::string _logdir;