]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
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 | |
b32b8144 | 27 | #include <cstdio> |
7c673cae FG |
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: | |
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 | ||
56 | ~parameter_cache() | |
57 | { | |
58 | #ifdef BOOST_COMPUTE_USE_OFFLINE_CACHE | |
59 | write_to_disk(); | |
60 | #endif // BOOST_COMPUTE_USE_OFFLINE_CACHE | |
61 | } | |
62 | ||
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 | ||
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 | ||
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 | |
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 | |
118 | static std::string version_string() | |
119 | { | |
120 | char buf[32]; | |
b32b8144 FG |
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 | |
7c673cae FG |
131 | return buf; |
132 | } | |
133 | ||
134 | // returns the file path for the cached parameters | |
135 | 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 | |
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 | |
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 |