Utility library
MathiasMagnus opened this issue · 0 comments
MathiasMagnus commented
In #1 I touched upon the issue of how to present utilities from the SDK to the users.
The way I imagine the OpenCL SDK hosted by Khronos, is that it provides all the necessary files to compile an OpenCL application (the ICD loader and headers) as well as a utility library which applications could rely on to take care of some tedium in the API.
Consume via CMake
The way I imagined this was to extend the current CMake namespace with the SDK library. Applications consuming both sub-projects may do so via:
find_package(OpenCL 3.0
REQUIRED
COMPONENTS
OpenCL
SDK
)
add_executable(my_app ${SOURCES})
target_link_libraries(my_app
PRIVATE
OpenCL::OpenCL
OpenCL::SDK
)
Utilities
Here is a short list of things I've grown to use in a few applications of mine.
profiling a la std::chrono
std::cout <<
"Device (kernel) execution took: " <<
cl::util::get_duration<CL_PROFILING_COMMAND_START,
CL_PROFILING_COMMAND_END,
std::chrono::microseconds>(kernel_event).count() <<
" us." << std::endl;
implementation
namespace cl::util
{
template <cl_int From, cl_int To, typename Dur = std::chrono::nanoseconds>
auto get_duration(cl::Event& ev)
{
return std::chrono::duration_cast<Dur>(std::chrono::nanoseconds{ ev.getProfilingInfo<To>() - ev.getProfilingInfo<From>() });
}
}
Iterator-based entry point
This I would like to propose as well for opencl.hpp
std::vector<cl::Platform> platforms(cl::util::Platform::begin(),
cl::util::Platform::end());
implementation
namespace cl::util
{
namespace Platform
{
class PlatformIterator;
PlatformIterator begin();
PlatformIterator end();
cl_uint count()
{
cl_int _err = CL_SUCCESS;
cl_uint _numPlatforms = 0;
_err = clGetPlatformIDs(0, nullptr, &_numPlatforms);
if (_err != CL_SUCCESS) throw cl::Error{ _err, "clGetPlatformIDs(0, nullptr, &cl_uint)" };
return _numPlatforms;
}
class PlatformIterator
{
friend PlatformIterator begin();
friend PlatformIterator end();
public:
typedef std::input_iterator_tag iterator_category;
typedef cl::Platform value_type;
typedef std::ptrdiff_t difference_type;
typedef cl::Platform* pointer;
typedef cl::Platform reference;
PlatformIterator& operator++() { ++_curr; return *this; }
PlatformIterator operator++(int) { auto res = *this; ++_curr; return res; }
reference operator*() const { return _plats.at(_curr); }
pointer operator->() const;
bool operator==(const PlatformIterator& b) const { return _curr == b._curr; }
bool operator!=(const PlatformIterator& b) const { return _curr != b._curr; }
private:
cl_uint _curr;
std::vector<cl::Platform> _plats;
PlatformIterator() : PlatformIterator{ 0 } {}
PlatformIterator(cl_uint curr)
: _curr{ curr }
{
if (_curr != count()) cl::Platform::get(&_plats);
}
};
PlatformIterator begin() { return PlatformIterator{ 0 }; }
PlatformIterator end() { return PlatformIterator{ count() }; }
}
}
Obtain executable path
auto kernel_path = cl::util::get_exe_path().parent_path().append("saxpy.cl");
std::ifstream source_file{ kernel_path };
if (!source_file.is_open())
throw std::runtime_error{ std::string{ "Cannot open kernel source: " } + kernel_path.generic_string() };
implementation
// Header //
#if __has_include(<filesystem>)
#include <filesystem>
#elif __has_include(<experimental/filesystem>)
#include <experimental/filesystem>
namespace std {
namespace filesystem = experimental::filesystem;
}
#else
#error "No STL filesystem support could be detected"
#endif
namespace cl::util
{
std::filesystem::path get_exe_path();
}
// Source //
#ifdef _WIN32
#include <Windows.h> //GetModuleFileNameW
#else
#include <limits.h>
#include <unistd.h> //readlink
#endif
std::filesystem::path cl::util::get_exe_path()
{
#ifdef _WIN32
wchar_t path[MAX_PATH] = { 0 };
GetModuleFileNameW(NULL, path, MAX_PATH);
return path;
#else
char result[PATH_MAX];
ssize_t count = readlink("/proc/self/exe", result, PATH_MAX);
return std::string(result, (count > 0) ? count : 0);
#endif
}