Skip to content

Commit

Permalink
Offline cache for compiled kernels
Browse files Browse the repository at this point in the history
vex::build_sources() dumps compiled program binaries to filesystem.
These are automatically reused in subsequent program launches. Binaries
are stored in ~/.vexcl on Linux and in %APPDATA%/vexcl on Windows. File
names are SHA1 hashes of concatenated sourses, device name and platform,
and compile options.

The caching results on drop of time for running all tests (on AMD
platform) from 34 to 2 seconds.  However this could also result in some
unforseen problems, so this is disabled by default. In order to enable
offline kernel caching, user has to define VEXCL_CACHE_KERNELS
preprocessor macro.

Note that this does not affect online kernel caching, which is always
enabled.
  • Loading branch information
ddemidov committed Sep 14, 2013
1 parent d192b89 commit 1aedcd2
Show file tree
Hide file tree
Showing 2 changed files with 140 additions and 2 deletions.
5 changes: 5 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ if (VEXCL_SHOW_KERNELS)
add_definitions(-DVEXCL_SHOW_KERNELS)
endif (VEXCL_SHOW_KERNELS)

option(VEXCL_CACHE_KERNELS "Cache compiled kernels offline")
if (VEXCL_CACHE_KERNELS)
add_definitions(-DVEXCL_CACHE_KERNELS)
endif (VEXCL_CACHE_KERNELS)

#----------------------------------------------------------------------------
# Find Boost
#----------------------------------------------------------------------------
Expand Down
137 changes: 135 additions & 2 deletions vexcl/util.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,16 @@ THE SOFTWARE.

#include <boost/config.hpp>

#ifdef VEXCL_CACHE_KERNELS
# include <fstream>
# include <sstream>
# include <iomanip>
# include <cstdlib>
# include <boost/uuid/sha1.hpp>
# include <boost/optional.hpp>
# include <boost/filesystem.hpp>
#endif

#ifdef BOOST_NO_VARIADIC_TEMPLATES
# include <boost/proto/proto.hpp>
# include <boost/preprocessor/repetition.hpp>
Expand Down Expand Up @@ -243,7 +253,106 @@ inline std::string standard_kernel_header(const cl::Device &dev) {
) + get_program_header(dev);
}

#ifdef VEXCL_CACHE_KERNELS
/// Path delimiter symbol.
inline const std::string& path_delim() {
static const std::string delim = boost::filesystem::path("/").make_preferred().native();
return delim;
}

/// Path to appdata folder.
inline const std::string& appdata_path() {
#ifdef WIN32
# ifdef _MSC_VER
# pragma warning(push)
# pragma warning(disable: 4996)
# endif
static const std::string appdata = getenv("APPDATA") + path_delim() + "vexcl";
# ifdef _MSC_VER
# pragma warning(pop)
# endif
#else
static const std::string appdata = getenv("HOME") + path_delim() + ".vexcl";
#endif
return appdata;
}

/// Path to cached binaries.
inline std::string program_binaries_path(const std::string &hash, bool create = false)
{
std::string dir = appdata_path() + path_delim() + hash.substr(0, 2);
if (create) boost::filesystem::create_directories(dir);
return dir + path_delim() + hash.substr(2);
}

/// Saves program binaries for future reuse.
inline void save_program_binaries(const std::string &hash, const cl::Program &program)
{
std::ofstream bfile(program_binaries_path(hash, true), std::ios::binary);
if (!bfile) return;

std::vector<size_t> sizes = program.getInfo<CL_PROGRAM_BINARY_SIZES>();
std::vector<char*> binaries = program.getInfo<CL_PROGRAM_BINARIES>();

assert(sizes.size() == 1);

bfile.write((char*)&sizes[0], sizeof(size_t));
bfile.write(binaries[0], sizes[0]);
delete[] binaries[0];
}

/// Tries to read program binaries from file cache.
inline boost::optional<cl::Program> load_program_binaries(
const std::string &hash, const cl::Context &context,
const std::vector<cl::Device> &device
)
{
std::ifstream bfile(program_binaries_path(hash), std::ios::binary);
if (!bfile) return boost::optional<cl::Program>();

size_t n;
std::vector<char> buf;

bfile.read((char*)&n, sizeof(size_t));
buf.resize(n);
bfile.read(buf.data(), n);

cl::Program program(context, device, cl::Program::Binaries(
1, std::make_pair(static_cast<const void*>(buf.data()), n)));

try {
program.build(device, "");
} catch(const cl::Error&) {
std::cerr << "Loading binaries failed:" << std::endl
<< program.getBuildInfo<CL_PROGRAM_BUILD_LOG>(device[0])
<< std::endl;
return boost::optional<cl::Program>();
}

return boost::optional<cl::Program>(program);
}

/// Returns SHA1 hash of the string parameter.
inline std::string sha1(const std::string &src) {
boost::uuids::detail::sha1 sha1;
sha1.process_bytes(src.c_str(), src.size());

unsigned int hash[5];
sha1.get_digest(hash);

std::ostringstream buf;
for(int i = 0; i < 5; ++i)
buf << std::hex << std::setfill('0') << std::setw(8) << hash[i];

return buf.str();
}
#endif

/// Create and build a program from source string.
/**
* If VEXCL_CACHE_KERNELS macro is defined, then program binaries are cached
* in filesystem and reused in the following runs.
*/
inline cl::Program build_sources(
const cl::Context &context, const std::string &source,
const std::string &options = ""
Expand All @@ -253,12 +362,31 @@ inline cl::Program build_sources(
std::cout << source << std::endl;
#endif

auto device = context.getInfo<CL_CONTEXT_DEVICES>();
std::string compile_options = options + " " + get_compile_options(device[0]);

#ifdef VEXCL_CACHE_KERNELS
// Get unique (hopefully) hash string for the kernel.
std::ostringstream hashsrc;

hashsrc
<< source
<< compile_options
<< device[0].getInfo<CL_DEVICE_NAME>()
<< cl::Platform(device[0].getInfo<CL_DEVICE_PLATFORM>()).getInfo<CL_PLATFORM_NAME>();

std::string hash = sha1( hashsrc.str() );

// Try to get cached program binaries:
if (boost::optional<cl::Program> program = load_program_binaries(hash, context, device))
return *program;
#endif

// If cache is not available, just compile the sources.
cl::Program program(context, cl::Program::Sources(
1, std::make_pair(source.c_str(), source.size())
));

auto device = context.getInfo<CL_CONTEXT_DEVICES>();

try {
program.build(device, (options + " " + get_compile_options(device[0])).c_str());
} catch(const cl::Error&) {
Expand All @@ -269,6 +397,11 @@ inline cl::Program build_sources(
throw;
}

#ifdef VEXCL_CACHE_KERNELS
// Save program binaries for future reuse:
save_program_binaries(hash, program);
#endif

return program;
}

Expand Down

0 comments on commit 1aedcd2

Please sign in to comment.