/// Compile and load a program from source string. inline vex::backend::program build_sources(const command_queue &q, const std::string &source, const std::string &options = "" ) { #ifdef VEXCL_SHOW_KERNELS std::cout << source << std::endl; #else if (getenv("VEXCL_SHOW_KERNELS")) std::cout << source << std::endl; #endif std::string compile_options = options + " " + get_compile_options(q); sha1_hasher sha1; sha1.process(source) .process(compile_options) .process(VEXCL_JIT_COMPILER) .process(VEXCL_JIT_COMPILER_OPTIONS); std::string hash = static_cast<std::string>(sha1); // Write source to a .cpp file std::string basename = program_binaries_path(hash, true) + "kernel"; std::string sofile = basename + ".so"; if ( !boost::filesystem::exists(sofile) ) { std::string cppfile = basename + ".cpp"; { std::ofstream f(cppfile); f << source; } // Compile the source. std::ostringstream cmdline; cmdline << VEXCL_JIT_COMPILER << " -o " << sofile << " " << cppfile << " " << VEXCL_JIT_COMPILER_OPTIONS << " " << compile_options; if (0 != system(cmdline.str().c_str()) ) { #ifndef VEXCL_SHOW_KERNELS std::cerr << source << std::endl; #endif vex::detail::print_backtrace(); throw std::runtime_error("Kernel compilation failed"); } } // Load the compiled shared library. return boost::dll::shared_library(sofile); }
/** * If VEXCL_CACHE_KERNELS macro is defined, then program binaries are cached * in filesystem and reused in the following runs. */ inline boost::compute::program build_sources( const boost::compute::command_queue &queue, const std::string &source, const std::string &options = "" ) { #ifdef VEXCL_SHOW_KERNELS std::cout << source << std::endl; #else if (getenv("VEXCL_SHOW_KERNELS")) std::cout << source << std::endl; #endif return boost::compute::program::build_with_source( source, queue.get_context(), options + " " + get_compile_options(queue) ); }
/** * 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::CommandQueue &queue, const std::string &source, const std::string &options = "" ) { #ifdef VEXCL_SHOW_KERNELS std::cout << source << std::endl; #else if (getenv("VEXCL_SHOW_KERNELS")) std::cout << source << std::endl; #endif auto context = queue.getInfo<CL_QUEUE_CONTEXT>(); auto device = context.getInfo<CL_CONTEXT_DEVICES>(); std::string compile_options = options + " " + get_compile_options(queue); #ifdef VEXCL_CACHE_KERNELS // Get unique (hopefully) hash string for the kernel. std::ostringstream compiler_tag; compiler_tag #if defined(_MSC_VER) << "MSC " << _MSC_VER #elif defined(__clang__) << "Clang " << __clang_major__ << "." << __clang_minor__ #elif defined(__GNUC__) << "g++ " << __GNUC__ << "." << __GNUC_MINOR__ #else << "unknown" #endif ; sha1_hasher sha1; sha1.process(source) .process(cl::Platform(device[0].getInfo<CL_DEVICE_PLATFORM>()).getInfo<CL_PLATFORM_NAME>()) .process(device[0].getInfo<CL_DEVICE_NAME>()) .process(compile_options) .process(compiler_tag.str()) ; std::string hash = static_cast<std::string>(sha1); // Try to get cached program binaries: try { if (boost::optional<cl::Program> program = load_program_binaries(hash, context, device, compile_options)) return *program; } catch (...) { // Shit happens. std::cerr << "Failed to load precompiled binaries" << std::endl; } #endif // If cache is not available, just compile the sources. cl::Program program(context, source); try { program.build(device, (options + " " + get_compile_options(queue)).c_str()); } catch(const cl::Error&) { std::cerr << source << std::endl << program.getBuildInfo<CL_PROGRAM_BUILD_LOG>(device[0]) << std::endl; vex::detail::print_backtrace(); throw; } #ifdef VEXCL_CACHE_KERNELS // Save program binaries for future reuse: save_program_binaries(hash, program); #endif return program; }
/** * 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::CommandQueue &queue, const std::string &source, const std::string &options = "" ) { #ifdef VEXCL_SHOW_KERNELS std::cout << source << std::endl; #else # ifdef _MSC_VER # pragma warning(push) # pragma warning(disable: 4996) # endif if (getenv("VEXCL_SHOW_KERNELS")) std::cout << source << std::endl; # ifdef _MSC_VER # pragma warning(pop) # endif #endif auto context = queue.getInfo<CL_QUEUE_CONTEXT>(); auto device = context.getInfo<CL_CONTEXT_DEVICES>(); std::string compile_options = options + " " + get_compile_options(queue); #ifdef VEXCL_CACHE_KERNELS // Get unique (hopefully) hash string for the kernel. std::ostringstream fullsrc; fullsrc << "// Platform: " << cl::Platform(device[0].getInfo<CL_DEVICE_PLATFORM>()).getInfo<CL_PLATFORM_NAME>() << "\n// Device: " << device[0].getInfo<CL_DEVICE_NAME>() << "\n// Compiler: " #if defined(_MSC_VER) << "MSC " << _MSC_VER #elif defined(__clang__) << "Clang " << __clang_major__ << "." << __clang_minor__ #elif defined(__GNUC__) << "g++ " << __GNUC__ << "." << __GNUC_MINOR__ #else << "unknown" #endif << "\n// options: " << compile_options << "\n" << source; std::string hash = sha1( fullsrc.str() ); // Try to get cached program binaries: try { if (boost::optional<cl::Program> program = load_program_binaries(hash, context, device)) return *program; } catch (...) { // Shit happens. } #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()) )); try { program.build(device, (options + " " + get_compile_options(queue)).c_str()); } catch(const cl::Error&) { std::cerr << source << std::endl << program.getBuildInfo<CL_PROGRAM_BUILD_LOG>(device[0]) << std::endl; throw; } #ifdef VEXCL_CACHE_KERNELS // Save program binaries for future reuse: save_program_binaries(hash, program, fullsrc.str()); #endif return program; }
/// Create and build a program from source string. inline vex::backend::program build_sources( const command_queue &queue, const std::string &source, const std::string &options = "" ) { #ifdef VEXCL_SHOW_KERNELS std::cout << source << std::endl; #else if (getenv("VEXCL_SHOW_KERNELS")) std::cout << source << std::endl; #endif std::string compile_options = options + " " + get_compile_options(queue); queue.context().set_current(); auto cc = queue.device().compute_capability(); std::ostringstream ccstr; ccstr << std::get<0>(cc) << std::get<1>(cc); sha1_hasher sha1; sha1.process(source) .process(queue.device().name()) .process(compile_options) .process(ccstr.str()) ; std::string hash = static_cast<std::string>(sha1); // Write source to a .cu file std::string basename = program_binaries_path(hash, true) + "kernel"; std::string ptxfile = basename + ".ptx"; if ( !boost::filesystem::exists(ptxfile) ) { std::string cufile = basename + ".cu"; { std::ofstream f(cufile); f << source; } // Compile the source to ptx. std::ostringstream cmdline; cmdline << "nvcc -ptx -O3" << " -arch=sm_" << std::get<0>(cc) << std::get<1>(cc) << " " << compile_options << " -o " << ptxfile << " " << cufile; if (0 != system(cmdline.str().c_str()) ) { #ifndef VEXCL_SHOW_KERNELS std::cerr << source << std::endl; #endif vex::detail::print_backtrace(); throw std::runtime_error("nvcc invocation failed"); } } // Load the compiled ptx. CUmodule prg; cuda_check( cuModuleLoad(&prg, ptxfile.c_str()) ); return program(queue.context(), prg); }