parameter_cache.hpp 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. //---------------------------------------------------------------------------//
  2. // Copyright (c) 2013-2015 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_DETAIL_PARAMETER_CACHE_HPP
  11. #define BOOST_COMPUTE_DETAIL_PARAMETER_CACHE_HPP
  12. #include <algorithm>
  13. #include <string>
  14. #include <boost/shared_ptr.hpp>
  15. #include <boost/make_shared.hpp>
  16. #include <boost/noncopyable.hpp>
  17. #include <boost/compute/config.hpp>
  18. #include <boost/compute/device.hpp>
  19. #include <boost/compute/detail/global_static.hpp>
  20. #include <boost/compute/version.hpp>
  21. #ifdef BOOST_COMPUTE_USE_OFFLINE_CACHE
  22. #include <cstdio>
  23. #include <boost/algorithm/string/trim.hpp>
  24. #include <boost/compute/detail/path.hpp>
  25. #include <boost/property_tree/ptree.hpp>
  26. #include <boost/property_tree/json_parser.hpp>
  27. #endif // BOOST_COMPUTE_USE_OFFLINE_CACHE
  28. namespace boost {
  29. namespace compute {
  30. namespace detail {
  31. class parameter_cache : boost::noncopyable
  32. {
  33. public:
  34. parameter_cache(const device &device)
  35. : m_dirty(false),
  36. m_device_name(device.name())
  37. {
  38. #ifdef BOOST_COMPUTE_USE_OFFLINE_CACHE
  39. // get offline cache file name (e.g. /home/user/.boost_compute/tune/device.json)
  40. m_file_name = make_file_name();
  41. // load parameters from offline cache file (if it exists)
  42. if(boost::filesystem::exists(m_file_name)){
  43. read_from_disk();
  44. }
  45. #endif // BOOST_COMPUTE_USE_OFFLINE_CACHE
  46. }
  47. ~parameter_cache()
  48. {
  49. #ifdef BOOST_COMPUTE_USE_OFFLINE_CACHE
  50. write_to_disk();
  51. #endif // BOOST_COMPUTE_USE_OFFLINE_CACHE
  52. }
  53. void set(const std::string &object, const std::string &parameter, uint_ value)
  54. {
  55. m_cache[std::make_pair(object, parameter)] = value;
  56. // set the dirty flag to true. this will cause the updated parameters
  57. // to be stored to disk.
  58. m_dirty = true;
  59. }
  60. uint_ get(const std::string &object, const std::string &parameter, uint_ default_value)
  61. {
  62. std::map<std::pair<std::string, std::string>, uint_>::iterator
  63. iter = m_cache.find(std::make_pair(object, parameter));
  64. if(iter != m_cache.end()){
  65. return iter->second;
  66. }
  67. else {
  68. return default_value;
  69. }
  70. }
  71. static boost::shared_ptr<parameter_cache> get_global_cache(const device &device)
  72. {
  73. // device name -> parameter cache
  74. typedef std::map<std::string, boost::shared_ptr<parameter_cache> > cache_map;
  75. BOOST_COMPUTE_DETAIL_GLOBAL_STATIC(cache_map, caches, ((std::less<std::string>())));
  76. cache_map::iterator iter = caches.find(device.name());
  77. if(iter == caches.end()){
  78. boost::shared_ptr<parameter_cache> cache =
  79. boost::make_shared<parameter_cache>(device);
  80. caches.insert(iter, std::make_pair(device.name(), cache));
  81. return cache;
  82. }
  83. else {
  84. return iter->second;
  85. }
  86. }
  87. private:
  88. #ifdef BOOST_COMPUTE_USE_OFFLINE_CACHE
  89. // returns a string containing a cannoical device name
  90. static std::string cannonical_device_name(std::string name)
  91. {
  92. boost::algorithm::trim(name);
  93. std::replace(name.begin(), name.end(), ' ', '_');
  94. std::replace(name.begin(), name.end(), '(', '_');
  95. std::replace(name.begin(), name.end(), ')', '_');
  96. return name;
  97. }
  98. // returns the boost.compute version string
  99. static std::string version_string()
  100. {
  101. char buf[32];
  102. // snprintf is in Visual Studio since Visual Studio 2015 (_MSC_VER == 1900)
  103. #if defined (_MSC_VER) && _MSC_VER < 1900
  104. #define DETAIL_SNPRINTF sprintf_s
  105. #else
  106. #define DETAIL_SNPRINTF std::snprintf
  107. #endif
  108. DETAIL_SNPRINTF(buf, sizeof(buf), "%d.%d.%d", BOOST_COMPUTE_VERSION_MAJOR,
  109. BOOST_COMPUTE_VERSION_MINOR,
  110. BOOST_COMPUTE_VERSION_PATCH);
  111. #undef DETAIL_SNPRINTF
  112. return buf;
  113. }
  114. // returns the file path for the cached parameters
  115. std::string make_file_name() const
  116. {
  117. return detail::parameter_cache_path(true) + cannonical_device_name(m_device_name) + ".json";
  118. }
  119. // store current parameters to disk
  120. void write_to_disk()
  121. {
  122. BOOST_ASSERT(!m_file_name.empty());
  123. if(m_dirty){
  124. // save current parameters to disk
  125. boost::property_tree::ptree pt;
  126. pt.put("header.device", m_device_name);
  127. pt.put("header.version", version_string());
  128. typedef std::map<std::pair<std::string, std::string>, uint_> map_type;
  129. for(map_type::const_iterator iter = m_cache.begin(); iter != m_cache.end(); ++iter){
  130. const std::pair<std::string, std::string> &key = iter->first;
  131. pt.add(key.first + "." + key.second, iter->second);
  132. }
  133. write_json(m_file_name, pt);
  134. m_dirty = false;
  135. }
  136. }
  137. // load stored parameters from disk
  138. void read_from_disk()
  139. {
  140. BOOST_ASSERT(!m_file_name.empty());
  141. m_cache.clear();
  142. boost::property_tree::ptree pt;
  143. try {
  144. read_json(m_file_name, pt);
  145. }
  146. catch(boost::property_tree::json_parser::json_parser_error&){
  147. // no saved cache file, ignore
  148. return;
  149. }
  150. std::string stored_device;
  151. try {
  152. stored_device = pt.get<std::string>("header.device");
  153. }
  154. catch(boost::property_tree::ptree_bad_path&){
  155. return;
  156. }
  157. std::string stored_version;
  158. try {
  159. stored_version = pt.get<std::string>("header.version");
  160. }
  161. catch(boost::property_tree::ptree_bad_path&){
  162. return;
  163. }
  164. if(stored_device == m_device_name && stored_version == version_string()){
  165. typedef boost::property_tree::ptree::const_iterator pt_iter;
  166. for(pt_iter iter = pt.begin(); iter != pt.end(); ++iter){
  167. if(iter->first == "header"){
  168. // skip header
  169. continue;
  170. }
  171. boost::property_tree::ptree child_pt = pt.get_child(iter->first);
  172. for(pt_iter child_iter = child_pt.begin(); child_iter != child_pt.end(); ++child_iter){
  173. set(iter->first, child_iter->first, boost::lexical_cast<uint_>(child_iter->second.data()));
  174. }
  175. }
  176. }
  177. m_dirty = false;
  178. }
  179. #endif // BOOST_COMPUTE_USE_OFFLINE_CACHE
  180. private:
  181. bool m_dirty;
  182. std::string m_device_name;
  183. std::string m_file_name;
  184. std::map<std::pair<std::string, std::string>, uint_> m_cache;
  185. };
  186. } // end detail namespace
  187. } // end compute namespace
  188. } // end boost namespace
  189. #endif // BOOST_COMPUTE_DETAIL_PARAMETER_CACHE_HPP