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