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 &parameter, 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 &parameter, 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