// opencl_cache.cpp #include // OpenCL C++ bindings #include #include #include #include #include #include // C++17 #include // ------------------------------------------------------------------- // Helpers // ------------------------------------------------------------------- // Read a text file into a string std::string read_file(const std::string &path) { std::ifstream f(path, std::ios::in | std::ios::binary); if (!f) throw std::runtime_error("Cannot open file: " + path); std::ostringstream ss; ss << f.rdbuf(); return ss.str(); } // Read a binary file into a std::vector std::vector read_binary(const std::string &path) { std::ifstream f(path, std::ios::binary); if (!f) throw std::runtime_error("Cannot open binary file: " + path); return std::vector(std::istreambuf_iterator(f), std::istreambuf_iterator()); } // Write a binary buffer to file void write_binary(const std::string &path, const std::vector &data) { std::ofstream f(path, std::ios::binary); if (!f) throw std::runtime_error("Cannot write binary file: " + path); f.write(reinterpret_cast(data.data()), data.size()); } // Create a unique key for the device (vendor + name + global mem size) std::string device_key(const cl::Device &dev) { std::ostringstream ss; ss << dev.getInfo() << "-" << dev.getInfo() << "-" << dev.getInfo(); return ss.str(); } // ------------------------------------------------------------------- // Main // ------------------------------------------------------------------- int main() { try { // 1) Pick the first GPU device on the first platform std::vector platforms; cl::Platform::get(&platforms); if (platforms.empty()) throw std::runtime_error("No OpenCL platforms found"); cl::Platform platform = platforms[0]; std::vector devices; platform.getDevices(CL_DEVICE_TYPE_GPU, &devices); if (devices.empty()) throw std::runtime_error("No GPU devices on the platform"); cl::Device device = devices[0]; std::cout << "Using platform: " << platform.getInfo() << '\n'; std::cout << "Using device : " << device.getInfo() << '\n'; // 2) Context & cache file name cl::Context ctx(device); std::string bin_file = device_key(device) + ".bin"; std::filesystem::path bin_path = std::filesystem::current_path() / bin_file; cl::Program program; // 3) If we already have a binary, load it if (std::filesystem::exists(bin_path)) { std::cout << "Loading binary from " << bin_path << '\n'; std::vector bin = read_binary(bin_path.string()); // OpenCL expects an array of *unsigned char* pointers, one per // device. We are using only one device here. std::vector binaries(1, bin.data()); std::vector lengths(1, bin.size()); std::vector progs(1); cl_int err = ctx.createProgramWithBinary(devices, lengths, binaries.data(), nullptr, &progs[0]); if (err != CL_SUCCESS) throw std::runtime_error("clCreateProgramWithBinary failed"); program = progs[0]; } else { // 4) Compile from source, extract the binary, save it std::cout << "Compiling source (first run)...\n"; std::string src = read_file("example.cl"); cl::Program::Sources srcs(1, { src.c_str(), src.length() + 1 }); program = cl::Program(ctx, srcs); cl_int err = program.build(devices); if (err != CL_SUCCESS) { std::string log = program.getBuildInfo(device); throw std::runtime_error("Program build failed: " + log); } // Retrieve the binary std::vector bin; std::vector lengths; program.getInfo(CL_PROGRAM_BINARY_SIZES, &lengths); program.getInfo(CL_PROGRAM_BINARIES, &bin); // Save it for next run write_binary(bin_path.string(), std::vector(bin.begin(), bin.end())); std::cout << "Saved binary to " << bin_path << '\n'; } // ------------------------------------------------------------------- // 5) Demo: add two float vectors // ------------------------------------------------------------------- size_t N = 1024; std::vector a(N, 1.0f), b(N, 2.0f), c(N, 0.0f); // Allocate device buffers cl::Buffer bufA(ctx, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, sizeof(float) * N, a.data()); cl::Buffer bufB(ctx, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, sizeof(float) * N, b.data()); cl::Buffer bufC(ctx, CL_MEM_WRITE_ONLY, sizeof(float) * N); // Create kernel cl::Kernel kernel(program, "add_vectors"); // Set arguments kernel.setArg(0, bufA); kernel.setArg(1, bufB); kernel.setArg(2, bufC); // Launch cl::CommandQueue queue(ctx, device); queue.enqueueNDRangeKernel(kernel, cl::NullRange, cl::NDRange(N), cl::NullRange); queue.finish(); // Read back queue.enqueueReadBuffer(bufC, CL_TRUE, 0, sizeof(float) * N, c.data()); // Verify a few elements bool ok = true; for (size_t i = 0; i < N; ++i) { if (c[i] != a[i] + b[i]) { ok = false; break; } } std::cout << "Demo " << (ok ? "succeeded" : "failed") << '\n'; } catch (const std::exception &e) { std::cerr << "ERROR: " << e.what() << '\n'; return 1; } return 0; }