libjmmcg  release_579_6_g8cffd
A C++ library containing an eclectic mix of useful, advanced components.
thread_statistics.hpp
Go to the documentation of this file.
1 #ifndef LIBJMMCG_CORE_THREAD_STATISTICS_HPP
2 #define LIBJMMCG_CORE_THREAD_STATISTICS_HPP
3 
4 /******************************************************************************
5 ** Copyright © 2011 by J.M.McGuiness, coder@hussar.me.uk
6 **
7 ** This library is free software; you can redistribute it and/or
8 ** modify it under the terms of the GNU Lesser General Public
9 ** License as published by the Free Software Foundation; either
10 ** version 2.1 of the License, or (at your option) any later version.
11 **
12 ** This library is distributed in the hope that it will be useful,
13 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
14 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 ** Lesser General Public License for more details.
16 **
17 ** You should have received a copy of the GNU Lesser General Public
18 ** License along with this library; if not, write to the Free Software
19 ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21 
23 
24 #include <boost/graph/adjacency_list.hpp>
25 #include <boost/graph/graphviz.hpp>
26 
27 namespace jmmcg { namespace LIBJMMCG_VER_NAMESPACE { namespace ppd {
28 
29  /// Just stub statistics - to support the interface. No actual statistics are collected.
30  /**
31  To gather basic statistics use basic_statistics, or roll your own!
32 
33  \see basic_statistics
34  */
35  template<class Sz>
36  struct no_statistics {
37  typedef Sz work_added_type;
39 
40  struct process_work_type;
41  typedef process_work_type vertical_statistics_type;
42  typedef process_work_type hrz_statistics_type;
43 
44  static constexpr void added_work() noexcept(true) FORCE_INLINE {}
45  constexpr static work_added_type __fastcall total_work_added() noexcept(true) FORCE_INLINE {return work_added_type();}
46 
47  constexpr void processed_vertical_work() noexcept(true) FORCE_INLINE {}
48  static constexpr void add_vertical_work(ave_stats_type const &) noexcept(true) FORCE_INLINE {}
49  static ave_stats_type const &__fastcall total_vertical_work() noexcept(true) {
50  static const ave_stats_type tmp;
51  return tmp;
52  }
53 
54  constexpr void processed_hrz_work() noexcept(true) FORCE_INLINE {}
55  static constexpr void add_hrz_work(ave_stats_type const &) noexcept(true) FORCE_INLINE {}
56  static ave_stats_type const &__fastcall total_hrz_work() noexcept(true) FORCE_INLINE {
57  static const ave_stats_type tmp;
58  return tmp;
59  }
60  static constexpr void update_max_queue_len(work_added_type const) noexcept(true) FORCE_INLINE {}
61  static constexpr void update_colln_stats(work_added_type const) noexcept(true) FORCE_INLINE {}
62 
63  friend constexpr tostream & __fastcall operator<<(tostream &os, no_statistics const &) noexcept(true) FORCE_INLINE {
64  return os;
65  }
66  };
67 
68  /// Some basic statistics collected about the operation of the wrapping thread_pool.
69  /**
70  Note that this class does not use add any locking nor wrap the work_added_type as an atomic object, therefore the updates to it may suffer from races, if the work_added_type is not itself atomic. This is by design: performance vs. accuracy and locking reduces performance, and this class is designed to be fast, not accurate, so the statistics gathered may be under-estimates.
71 
72  \todo Add work completed/sec.
73 
74  \see no_statistics
75  \see pool_aspect::statistics_type
76  */
77  template<class Sz>
79  public:
80  typedef Sz work_added_type;
82 
83  /**
84  Well, this is a bit strict, it could be an atomic integral-type quite happily.
85  */
86  static_assert(std::is_integral<work_added_type>::value, "The input type must be an integral type as defined in 3.9.1 of the Standard.");
87 
88  class process_work_type;
89  typedef process_work_type vertical_statistics_type;
90  typedef process_work_type hrz_statistics_type;
91 
92  constexpr basic_statistics() noexcept(true) FORCE_INLINE
93  : queue_stats(), vertical_work(), hrz_work(), colln_stats(), work_added() {
94  }
95 
96  void added_work() noexcept(true) FORCE_INLINE {
97  ++work_added;
98  }
99  work_added_type __fastcall total_work_added() const noexcept(true) FORCE_INLINE {
100  return work_added;
101  }
102 
103  void processed_vertical_work() noexcept(true) FORCE_INLINE {
104  vertical_work.update(1);
105  }
106  void add_vertical_work(ave_stats_type const &wk) noexcept(true) FORCE_INLINE {
107  vertical_work+=wk;
108  }
109  ave_stats_type const &__fastcall total_vertical_work() const noexcept(true) FORCE_INLINE {
110  return vertical_work;
111  }
112 
113  void processed_hrz_work() noexcept(true) FORCE_INLINE {
114  hrz_work.update(1);
115  }
116  void add_hrz_work(ave_stats_type const &wk) noexcept(true) FORCE_INLINE {
117  hrz_work+=wk;
118  }
119  ave_stats_type const &__fastcall total_hrz_work() const noexcept(true) {
120  return hrz_work;
121  }
122 
123  void update_max_queue_len(work_added_type const l) noexcept(true) FORCE_INLINE {
124  queue_stats.update(l);
125  }
126 
127  void update_colln_stats(work_added_type const l) noexcept(true) FORCE_INLINE {
128  colln_stats.update(l);
129  }
130 
131  /**
132  \todo Implement using the advice given in "Standard C++ IOStreams and Locales" by A.Langer & K.Kreft, page 170.
133  */
134  friend tostream & __fastcall operator<<(tostream &os, basic_statistics const &p) noexcept(false) {
135  os<<_T("work added=")<<p.work_added
136  <<_T(", vertical work: ")<<p.vertical_work
137  <<_T(", horizontal work: ")<<p.hrz_work
138  <<_T(", pool-queue statistics: ")<<p.queue_stats
139  <<_T(", data-parallel collection statistics: ")<<p.colln_stats;
140  return os;
141  }
142 
143  private:
144  ave_stats_type queue_stats;
145  ave_stats_type vertical_work;
146  ave_stats_type hrz_work;
147  ave_stats_type colln_stats;
148  work_added_type work_added;
149  };
150 
151  /// A dummy class to not generate a Control Flow Graph (CFG), and hopefully and hopefully all calls will be optimised out, so there should be no cost.
152  /**
153  \see control_flow_graph
154  */
155  template<class OST>
157  public:
158  typedef OST os_traits;
159  typedef typename os_traits::lock_traits lock_traits;
161  typedef std::string node_property_t;
162  typedef char edge_annotation_t;
163  typedef int container_type;
164  typedef int vertex_t;
165  typedef int edge_t;
166  template<class Node>
167  class add_cfg_details;
171 
172  constexpr no_control_flow_graph() noexcept(true) FORCE_INLINE {}
174 
175  static void write_graphviz(std::ostream &) noexcept(true) FORCE_INLINE {}
176  };
177 
178  /// A class to generate a Control Flow Graph (CFG), if the support has been coded into the joinable, nonjoinable, etc modifiers.
179  /**
180  Note that this class is coded for simplicity, not speed, so uses a "big lock" (the atomic_t type) to control multi-threaded access. This has at least these ramifications:
181 
182  1. All guarantees regarding number of locks & schedule generated by the library are invalidated (basic_statistics does not invalidate these guarantees).
183  2. The number of horizontal operations when generating a CFG may be different from that reported by basic_statistics::total_hrz_work() alone. In this case both results are right, but the number reported by basic_statistics::total_hrz_work() is an accurate measure; whereas the horizontal edges in this graph may have been affected by the internal locking within this class. To verify the CFG, compare the output of basic_statistics with both no_control_flow_graph and control_flow_graph in use, if they are the same you can be assured of accuracy. If different then you need to interpret the CFG output with greater care.
184 
185  \see no_control_flow_graph
186  */
187  template<class OST>
189  public:
190  typedef OST os_traits;
191  typedef typename os_traits::lock_traits lock_traits;
193 
194  /// Output the graph in DOTTY format.
195  /**
196  This may be converted to a pleasant PNG-format image using this command:
197  "nice dot -Tpng test.dot >test.png"
198  Assuming the os is created using an std::ofstream named "test.dot". Note that the PNG file can become large! thread_pool::merge() with 12 cores creates a file that is ~500kB in size and thread_pool::sort() is ~1Mb with 12 cores.
199  */
200  void write_graphviz(std::ostream &os) const noexcept(false) FORCE_INLINE {
201  const typename atomic_t::read_lock_type lk(lock_, atomic_t::lock_traits::infinite_timeout());
202  boost::write_graphviz(os, cfg_, boost::make_label_writer(vertex_names), boost::make_label_writer(edge_names));
203  }
204 
205  // All the stuff below is used for implementing support for the CFG in the code, both inside the library and if required in the user code...
206 
207  typedef std::string node_property_t;
208  typedef char edge_annotation_t;
209  typedef boost::adjacency_list<
210  boost::listS,
211  boost::vecS,
218  template<class Node>
219  class add_cfg_details;
220 
224 
227  vertex_names[main_]="main()";
228  }
230 
231  private:
232  template<class Node>
233  friend class add_cfg_details;
234 
235  mutable atomic_t lock_;
236 
237  vertex_t add_vertex_nolk(node_property_t const &details) noexcept(false) FORCE_INLINE {
238  const vertex_t v=boost::add_vertex(cfg_);
239  vertex_names[v]=details;
240  return v;
241  }
242 
243  std::pair<edge_t, bool> add_edge_nolk(vertex_t const &u, vertex_t const &v, node_property_t::value_type const details) noexcept(false) FORCE_INLINE {
244  const std::pair<edge_t, bool> added_edge=boost::add_edge(u, v, cfg_);
245  edge_names[added_edge.first]=details;
246  return added_edge;
247  }
248  std::pair<edge_t, bool> add_edge_to_root_nolk(vertex_t const &v, node_property_t::value_type const details) noexcept(false) FORCE_INLINE {
249  return add_edge_nolk(main_, v, details);
250  }
251 
252  void update_edge_nolk(vertex_t &v, node_property_t::value_type const e_details) FORCE_INLINE {
253  typedef typename boost::graph_traits<container_type>::in_edge_iterator in_edge_iterator;
254 
255  std::pair<in_edge_iterator, in_edge_iterator> edges=boost::in_edges(v, cfg_);
256  edge_names[*edges.first]=e_details;
257  }
258 
259  typedef boost::property_map<container_type, boost::vertex_name_t>::type vertex_names_t;
260  typedef boost::property_map<container_type, boost::edge_name_t>::type edge_names_t;
261 
262  container_type cfg_;
263  vertex_names_t vertex_names;
264  edge_names_t edge_names;
265  const vertex_t main_;
266  };
267 
268 } } }
269 
271 
272 #endif