system.hpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448
  1. //---------------------------------------------------------------------------//
  2. // Copyright (c) 2013 Kyle Lutz <kyle.r.lutz@gmail.com>
  3. //
  4. // Distributed under the Boost Software License, Version 1.0
  5. // See accompanying file LICENSE_1_0.txt or copy at
  6. // http://www.boost.org/LICENSE_1_0.txt
  7. //
  8. // See http://boostorg.github.com/compute for more information.
  9. //---------------------------------------------------------------------------//
  10. #ifndef BOOST_COMPUTE_SYSTEM_HPP
  11. #define BOOST_COMPUTE_SYSTEM_HPP
  12. #include <string>
  13. #include <vector>
  14. #include <cstdlib>
  15. #include <boost/throw_exception.hpp>
  16. #if defined(BOOST_COMPUTE_THREAD_SAFE)
  17. # if defined(BOOST_COMPUTE_USE_CPP11)
  18. # include <mutex>
  19. # include <thread>
  20. # include <atomic>
  21. # else
  22. # include <boost/thread/mutex.hpp>
  23. # include <boost/thread/lock_guard.hpp>
  24. # include <boost/atomic.hpp>
  25. # endif
  26. #endif
  27. #include <boost/compute/cl.hpp>
  28. #include <boost/compute/device.hpp>
  29. #include <boost/compute/context.hpp>
  30. #include <boost/compute/platform.hpp>
  31. #include <boost/compute/command_queue.hpp>
  32. #include <boost/compute/detail/getenv.hpp>
  33. #include <boost/compute/exception/no_device_found.hpp>
  34. #include <boost/compute/exception/context_error.hpp>
  35. namespace boost {
  36. namespace compute {
  37. /// \class system
  38. /// \brief Provides access to platforms and devices on the system.
  39. ///
  40. /// The system class contains a set of static functions which provide access to
  41. /// the OpenCL platforms and compute devices on the host system.
  42. ///
  43. /// The default_device() convenience method automatically selects and returns
  44. /// the "best" compute device for the system following a set of heuristics and
  45. /// environment variables. This simplifies setup of the OpenCL enviornment.
  46. ///
  47. /// \see platform, device, context
  48. class system
  49. {
  50. public:
  51. /// Returns the default compute device for the system.
  52. ///
  53. /// The default device is selected based on a set of heuristics and can be
  54. /// influenced using one of the following environment variables:
  55. ///
  56. /// \li \c BOOST_COMPUTE_DEFAULT_DEVICE -
  57. /// name of the compute device (e.g. "GTX TITAN")
  58. /// \li \c BOOST_COMPUTE_DEFAULT_DEVICE_TYPE
  59. /// type of the compute device (e.g. "GPU" or "CPU")
  60. /// \li \c BOOST_COMPUTE_DEFAULT_PLATFORM -
  61. /// name of the platform (e.g. "NVIDIA CUDA")
  62. /// \li \c BOOST_COMPUTE_DEFAULT_VENDOR -
  63. /// name of the device vendor (e.g. "NVIDIA")
  64. /// \li \c BOOST_COMPUTE_DEFAULT_ENFORCE -
  65. /// If this is set to "1", then throw a no_device_found() exception
  66. /// if any of the above environment variables is set, but a matching
  67. /// device was not found.
  68. ///
  69. /// The default device is determined once on the first time this function
  70. /// is called. Calling this function multiple times will always result in
  71. /// the same device being returned.
  72. ///
  73. /// If no OpenCL device is found on the system, a no_device_found exception
  74. /// is thrown.
  75. ///
  76. /// For example, to print the name of the default compute device on the
  77. /// system:
  78. /// \code
  79. /// // get the default compute device
  80. /// boost::compute::device device = boost::compute::system::default_device();
  81. ///
  82. /// // print the name of the device
  83. /// std::cout << "default device: " << device.name() << std::endl;
  84. /// \endcode
  85. static device default_device()
  86. {
  87. return init_default_device();
  88. }
  89. /// Returns the device with \p name.
  90. ///
  91. /// \throws no_device_found if no device with \p name is found.
  92. static device find_device(const std::string &name)
  93. {
  94. const std::vector<device> devices = system::devices();
  95. for(size_t i = 0; i < devices.size(); i++){
  96. const device& device = devices[i];
  97. if(device.name() == name){
  98. return device;
  99. }
  100. }
  101. BOOST_THROW_EXCEPTION(no_device_found());
  102. }
  103. /// Returns a vector containing all of the compute devices on
  104. /// the system.
  105. ///
  106. /// For example, to print out the name of each OpenCL-capable device
  107. /// available on the system:
  108. /// \code
  109. /// for(const auto &device : boost::compute::system::devices()){
  110. /// std::cout << device.name() << std::endl;
  111. /// }
  112. /// \endcode
  113. static std::vector<device> devices()
  114. {
  115. std::vector<device> devices;
  116. const std::vector<platform> platforms = system::platforms();
  117. for(size_t i = 0; i < platforms.size(); i++){
  118. const std::vector<device> platform_devices = platforms[i].devices();
  119. devices.insert(
  120. devices.end(), platform_devices.begin(), platform_devices.end()
  121. );
  122. }
  123. return devices;
  124. }
  125. /// Returns the number of compute devices on the system.
  126. static size_t device_count()
  127. {
  128. size_t count = 0;
  129. const std::vector<platform> platforms = system::platforms();
  130. for(size_t i = 0; i < platforms.size(); i++){
  131. count += platforms[i].device_count();
  132. }
  133. return count;
  134. }
  135. /// Returns the default context for the system.
  136. ///
  137. /// The default context is created for the default device on the system
  138. /// (as returned by default_device()).
  139. ///
  140. /// The default context is created once on the first time this function is
  141. /// called. Calling this function multiple times will always result in the
  142. /// same context object being returned.
  143. static context default_context()
  144. {
  145. return init_default_context();
  146. }
  147. /// Returns the default command queue for the system.
  148. ///
  149. /// If user-provided command queue is given, the system-wide default context
  150. /// and default device will be set up appropriately so that the default queue
  151. /// matches the default context and device.
  152. ///
  153. /// If the OpenCL context and device associated with user-provided command queue
  154. /// does not match the default context and device that have already been set,
  155. /// a set_default_queue_error exception is thrown. For example:
  156. ///
  157. /// \snippet test/test_attach_user_queue_error.cpp queue_mismatch
  158. ///
  159. /// The default queue is created once on the first time this function is
  160. /// called. Calling this function multiple times will always result in the
  161. /// same command queue object being returned.
  162. static command_queue& default_queue(const command_queue &user_queue = command_queue())
  163. {
  164. return init_default_queue(user_queue);
  165. }
  166. /// Blocks until all outstanding computations on the default
  167. /// command queue are complete.
  168. ///
  169. /// This is equivalent to:
  170. /// \code
  171. /// system::default_queue().finish();
  172. /// \endcode
  173. static void finish()
  174. {
  175. default_queue().finish();
  176. }
  177. /// Returns a vector containing each of the OpenCL platforms on the system.
  178. ///
  179. /// For example, to print out the name of each OpenCL platform present on
  180. /// the system:
  181. /// \code
  182. /// for(const auto &platform : boost::compute::system::platforms()){
  183. /// std::cout << platform.name() << std::endl;
  184. /// }
  185. /// \endcode
  186. static std::vector<platform> platforms()
  187. {
  188. cl_uint count = 0;
  189. clGetPlatformIDs(0, 0, &count);
  190. std::vector<platform> platforms;
  191. if(count > 0)
  192. {
  193. std::vector<cl_platform_id> platform_ids(count);
  194. clGetPlatformIDs(count, &platform_ids[0], 0);
  195. for(size_t i = 0; i < platform_ids.size(); i++){
  196. platforms.push_back(platform(platform_ids[i]));
  197. }
  198. }
  199. return platforms;
  200. }
  201. /// Returns the number of compute platforms on the system.
  202. static size_t platform_count()
  203. {
  204. cl_uint count = 0;
  205. clGetPlatformIDs(0, 0, &count);
  206. return static_cast<size_t>(count);
  207. }
  208. private:
  209. /// \internal_
  210. static device find_default_device()
  211. {
  212. // get a list of all devices on the system
  213. const std::vector<device> devices_ = devices();
  214. if(devices_.empty()){
  215. BOOST_THROW_EXCEPTION(no_device_found());
  216. }
  217. // check for device from environment variable
  218. const char *name = detail::getenv("BOOST_COMPUTE_DEFAULT_DEVICE");
  219. const char *type = detail::getenv("BOOST_COMPUTE_DEFAULT_DEVICE_TYPE");
  220. const char *platform = detail::getenv("BOOST_COMPUTE_DEFAULT_PLATFORM");
  221. const char *vendor = detail::getenv("BOOST_COMPUTE_DEFAULT_VENDOR");
  222. const char *enforce = detail::getenv("BOOST_COMPUTE_DEFAULT_ENFORCE");
  223. if(name || type || platform || vendor){
  224. for(size_t i = 0; i < devices_.size(); i++){
  225. const device& device = devices_[i];
  226. if (name && !matches(device.name(), name))
  227. continue;
  228. if (type && matches(std::string("GPU"), type))
  229. if (!(device.type() & device::gpu))
  230. continue;
  231. if (type && matches(std::string("CPU"), type))
  232. if (!(device.type() & device::cpu))
  233. continue;
  234. if (platform && !matches(device.platform().name(), platform))
  235. continue;
  236. if (vendor && !matches(device.vendor(), vendor))
  237. continue;
  238. return device;
  239. }
  240. if(enforce && enforce[0] == '1')
  241. BOOST_THROW_EXCEPTION(no_device_found());
  242. }
  243. // find the first gpu device
  244. for(size_t i = 0; i < devices_.size(); i++){
  245. const device& device = devices_[i];
  246. if(device.type() & device::gpu){
  247. return device;
  248. }
  249. }
  250. // find the first cpu device
  251. for(size_t i = 0; i < devices_.size(); i++){
  252. const device& device = devices_[i];
  253. if(device.type() & device::cpu){
  254. return device;
  255. }
  256. }
  257. // return the first device found
  258. return devices_[0];
  259. }
  260. /// \internal_
  261. static bool matches(const std::string &str, const std::string &pattern)
  262. {
  263. return str.find(pattern) != std::string::npos;
  264. }
  265. /// \internal_
  266. static device init_default_device(const device &user_device = device())
  267. {
  268. static device default_device;
  269. #ifdef BOOST_COMPUTE_THREAD_SAFE
  270. #ifdef BOOST_COMPUTE_USE_CPP11
  271. using namespace std;
  272. #else
  273. using namespace boost;
  274. #endif
  275. static atomic<bool> is_init;
  276. static mutex init_mutex;
  277. bool is_init_value = is_init.load(memory_order_consume);
  278. if (!is_init_value)
  279. {
  280. lock_guard<mutex> lock(init_mutex);
  281. is_init_value = is_init.load(memory_order_consume);
  282. if (!is_init_value)
  283. {
  284. default_device = user_device.get() ?
  285. user_device : find_default_device();
  286. is_init.store(true, memory_order_release);
  287. }
  288. }
  289. #else // BOOST_COMPUTE_THREAD_SAFE
  290. if (!default_device.get())
  291. {
  292. default_device = user_device.get() ?
  293. user_device : find_default_device();
  294. }
  295. #endif // BOOST_COMPUTE_THREAD_SAFE
  296. return default_device;
  297. }
  298. /// \internal_
  299. static context init_default_context(const context &user_context = context())
  300. {
  301. static context default_context;
  302. #ifdef BOOST_COMPUTE_THREAD_SAFE
  303. #ifdef BOOST_COMPUTE_USE_CPP11
  304. using namespace std;
  305. #else
  306. using namespace boost;
  307. #endif
  308. static atomic<bool> is_init;
  309. static mutex init_mutex;
  310. bool is_init_value = is_init.load(memory_order_consume);
  311. if (!is_init_value)
  312. {
  313. lock_guard<mutex> lock(init_mutex);
  314. is_init_value = is_init.load(memory_order_consume);
  315. if (!is_init_value)
  316. {
  317. default_context = user_context.get() ?
  318. user_context : context(default_device());
  319. is_init.store(true, memory_order_release);
  320. }
  321. }
  322. #else // BOOST_COMPUTE_THREAD_SAFE
  323. if (!default_context.get())
  324. {
  325. default_context = user_context.get() ?
  326. user_context : context(default_device());
  327. }
  328. #endif // BOOST_COMPUTE_THREAD_SAFE
  329. return default_context;
  330. }
  331. /// \internal_
  332. static void init_default_device_and_context(const command_queue &user_queue)
  333. {
  334. device user_device = user_queue.get_device();
  335. context user_context = user_queue.get_context();
  336. if ( (user_device != init_default_device(user_device)) ||
  337. (user_context != init_default_context(user_context)) )
  338. {
  339. // Try invoking default_queue() before anything else
  340. BOOST_THROW_EXCEPTION(set_default_queue_error());
  341. }
  342. }
  343. /// \internal_
  344. static command_queue& init_default_queue(const command_queue &user_queue = command_queue())
  345. {
  346. static command_queue default_queue;
  347. #ifdef BOOST_COMPUTE_THREAD_SAFE
  348. #ifdef BOOST_COMPUTE_USE_CPP11
  349. using namespace std;
  350. #else
  351. using namespace boost;
  352. #endif
  353. static atomic<bool> is_init;
  354. static mutex init_mutex;
  355. bool is_init_value = is_init.load(memory_order_consume);
  356. if (!is_init_value)
  357. {
  358. lock_guard<mutex> lock(init_mutex);
  359. is_init_value = is_init.load(memory_order_consume);
  360. if (!is_init_value)
  361. {
  362. if (user_queue.get())
  363. init_default_device_and_context(user_queue);
  364. default_queue = user_queue.get() ?
  365. user_queue :
  366. command_queue(default_context(), default_device());
  367. is_init.store(true, memory_order_release);
  368. }
  369. }
  370. #else // BOOST_COMPUTE_THREAD_SAFE
  371. if (!default_queue.get())
  372. {
  373. if (user_queue.get())
  374. init_default_device_and_context(user_queue);
  375. default_queue = user_queue.get() ?
  376. user_queue :
  377. command_queue(default_context(), default_device());
  378. }
  379. #endif // BOOST_COMPUTE_THREAD_SAFE
  380. else
  381. {
  382. BOOST_ASSERT_MSG(user_queue.get() == 0,
  383. "Default command queue has already been set.");
  384. }
  385. return default_queue;
  386. }
  387. };
  388. } // end compute namespace
  389. } // end boost namespace
  390. #endif // BOOST_COMPUTE_SYSTEM_HPP