On Tue, Dec 23, 2014 at 7:29 PM, Kyle Lutz
On Tue, Dec 23, 2014 at 1:20 AM, Andrey Semashev
wrote: 1. When you define a kernel (e.g. with the BOOST_COMPUTE_FUNCTION macro), is this kernel supposed to be in C? Can it reference global (namespace scope) objects and other functions? Other kernels?
Yes, the source code for OpenCL kernels and functions is specified in OpenCL C which is a dialect of C99 with extensions for vectorized operations.
Does this mean that the compiler has to support OpenCL in order to be able to use Boost.Compute? Or its specific features? If yes, can this be mentioned in the docs (with the list of the affected features, if possible)? Also, I don't quite understand, how the kernel source code which I supply to BOOST_COMPUTE_FUNCTION is then compiled into kernel. Is this source code just stringized and not actually compiled when the application is built?
There are a few ways to specific kernel functions which reference global C++ values. One is the BOOST_COMPUTE_CLOSURE() macro [1] which works similarly to BOOST_COMPUTE_FUNCTION(), but also allows a lambda-like capture list of C++ values.
2. When is the kernel compiled and uploaded to the device? Is it possible to cache and reuse the compiled kernel?
If writing a custom kernel, the kernel is built when the "program::build()" method is called. Internally, the higher-level algorithms compile programs when they're needed and store them in a global program cache.
And yes, compiled program and kernel objects can be stored and re-used (this is strongly recommended). Boost.Compute provides the program_cache class [3] which is used stores frequently used programs as compiled objects.
So, e.g. a kernel defined with BOOST_COMPUTE_FUNCTION will be compiled when first used, and then saved in some global program_cache, is that correct? Also, captured arguments of BOOST_COMPUTE_CLOSURE will be evaluated only once, when the kernel is built?
3. Why is the library not thread-safe by default? I'd say, we're long past single-threaded systems now, and having to always define the config macro is a nuisance.
I would very much like to have it thread-safe by default. This is a problem however with keeping the library header-only and useable with C++03 compilers. The BOOST_COMPUTE_THREAD_SAFE macro basically just instructs Boost.Compute to use the C++11 "thread_local" specifier for global objects instead of "static". With C++03 compilers, this will use boost::thread_specific_ptr<> which then requires users to also link to Boost.Thread.
That said, I still don't think it's ideal and I am very open to ideas/patches which improve this.
Personally, I see no big problem with dependency on Boost.Thread in C++03. However, it is quite possible to use system API to implement TLS in header-only library. On POSIX systems it is quite trivial with pthread_once and pthread_key* API. On Windows you can use Interlocked* functions or Boost.Atomic to implement something similar to pthread_once and Tls* functions for the TLS itself. The tricky part is the TLS cleanup, which can be done with help of the Windows thread pool. You can use RegisterWaitForSingleObject to schedule a wait operation on the handle of the thread that sets the thread-local value. When the thread exits, the pool will invoke the callback you passed to RegisterWaitForSingleObject, where you can clean the TLS value. The important difference from thread_local and Boost.Thread is that the callback is called in a thread different from the one that initialized the TLS value, but for various cleanup routines this should not matter. You can see how it's done in Boost.Sync: https://github.com/boostorg/sync/blob/develop/include/boost/sync/detail/wait...