OLD | NEW |
| (Empty) |
1 // -*- C++ -*- | |
2 // Testing performance utilities for the C++ library testsuite. | |
3 // | |
4 // Copyright (C) 2003, 2004, 2005, 2007, 2008, 2009 | |
5 // Free Software Foundation, Inc. | |
6 // | |
7 // This file is part of the GNU ISO C++ Library. This library is free | |
8 // software; you can redistribute it and/or modify it under the | |
9 // terms of the GNU General Public License as published by the | |
10 // Free Software Foundation; either version 3, or (at your option) | |
11 // any later version. | |
12 // | |
13 // This library is distributed in the hope that it will be useful, | |
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 // GNU General Public License for more details. | |
17 // | |
18 // You should have received a copy of the GNU General Public License along | |
19 // with this library; see the file COPYING3. If not see | |
20 // <http://www.gnu.org/licenses/>. | |
21 // | |
22 | |
23 #ifndef _GLIBCXX_PERFORMANCE_H | |
24 #define _GLIBCXX_PERFORMANCE_H | |
25 | |
26 #include <sys/times.h> | |
27 #include <sys/resource.h> | |
28 #include <cstdlib> | |
29 #include <cstring> | |
30 #include <string> | |
31 #include <fstream> | |
32 #include <iomanip> | |
33 #include <typeinfo> | |
34 #include <stdexcept> | |
35 #include <sstream> | |
36 #include <cxxabi.h> | |
37 #include <testsuite_common_types.h> | |
38 | |
39 #ifdef __linux__ | |
40 #include <malloc.h> | |
41 #elif defined (__FreeBSD__) | |
42 extern "C" | |
43 { | |
44 struct mallinfo | |
45 { | |
46 int uordblks; | |
47 int hblkhd; | |
48 }; | |
49 | |
50 struct mallinfo | |
51 mallinfo(void) | |
52 { | |
53 struct mallinfo m = { (((size_t) sbrk (0) + 1023) / 1024), 0 }; | |
54 return m; | |
55 } | |
56 } | |
57 #elif !defined (__hpux__) | |
58 extern "C" | |
59 { | |
60 struct mallinfo | |
61 { | |
62 int uordblks; | |
63 int hblkhd; | |
64 }; | |
65 | |
66 struct mallinfo empty = { 0, 0 }; | |
67 | |
68 struct mallinfo | |
69 mallinfo(void) | |
70 { return empty; } | |
71 } | |
72 #endif | |
73 | |
74 namespace __gnu_test | |
75 { | |
76 class time_counter | |
77 { | |
78 private: | |
79 clock_t elapsed_begin; | |
80 clock_t elapsed_end; | |
81 tms tms_begin; | |
82 tms tms_end; | |
83 | |
84 public: | |
85 explicit | |
86 time_counter() : elapsed_begin(), elapsed_end(), tms_begin(), tms_end() | |
87 { } | |
88 | |
89 void | |
90 clear() throw() | |
91 { | |
92 elapsed_begin = clock_t(); | |
93 elapsed_end = clock_t(); | |
94 tms_begin = tms(); | |
95 tms_end = tms(); | |
96 } | |
97 | |
98 void | |
99 start() | |
100 { | |
101 this->clear(); | |
102 elapsed_begin = times(&tms_begin); | |
103 const clock_t err = clock_t(-1); | |
104 if (elapsed_begin == err) | |
105 std::__throw_runtime_error("time_counter::start"); | |
106 } | |
107 | |
108 void | |
109 stop() | |
110 { | |
111 elapsed_end = times(&tms_end); | |
112 const clock_t err = clock_t(-1); | |
113 if (elapsed_end == err) | |
114 std::__throw_runtime_error("time_counter::stop"); | |
115 } | |
116 | |
117 size_t | |
118 real_time() const | |
119 { return elapsed_end - elapsed_begin; } | |
120 | |
121 size_t | |
122 user_time() const | |
123 { return tms_end.tms_utime - tms_begin.tms_utime; } | |
124 | |
125 size_t | |
126 system_time() const | |
127 { return tms_end.tms_stime - tms_begin.tms_stime; } | |
128 }; | |
129 | |
130 class resource_counter | |
131 { | |
132 int who; | |
133 rusage rusage_begin; | |
134 rusage rusage_end; | |
135 struct mallinfo allocation_begin; | |
136 struct mallinfo allocation_end; | |
137 | |
138 public: | |
139 resource_counter(int i = RUSAGE_SELF) : who(i) | |
140 { this->clear(); } | |
141 | |
142 void | |
143 clear() throw() | |
144 { | |
145 memset(&rusage_begin, 0, sizeof(rusage_begin)); | |
146 memset(&rusage_end, 0, sizeof(rusage_end)); | |
147 memset(&allocation_begin, 0, sizeof(allocation_begin)); | |
148 memset(&allocation_end, 0, sizeof(allocation_end)); | |
149 } | |
150 | |
151 void | |
152 start() | |
153 { | |
154 if (getrusage(who, &rusage_begin) != 0 ) | |
155 memset(&rusage_begin, 0, sizeof(rusage_begin)); | |
156 malloc(0); // Needed for some implementations. | |
157 allocation_begin = mallinfo(); | |
158 } | |
159 | |
160 void | |
161 stop() | |
162 { | |
163 if (getrusage(who, &rusage_end) != 0 ) | |
164 memset(&rusage_end, 0, sizeof(rusage_end)); | |
165 allocation_end = mallinfo(); | |
166 } | |
167 | |
168 int | |
169 allocated_memory() const | |
170 { return ((allocation_end.uordblks - allocation_begin.uordblks) | |
171 + (allocation_end.hblkhd - allocation_begin.hblkhd)); } | |
172 | |
173 long | |
174 hard_page_fault() const | |
175 { return rusage_end.ru_majflt - rusage_begin.ru_majflt; } | |
176 | |
177 long | |
178 swapped() const | |
179 { return rusage_end.ru_nswap - rusage_begin.ru_nswap; } | |
180 }; | |
181 | |
182 inline void | |
183 start_counters(time_counter& t, resource_counter& r) | |
184 { | |
185 t.start(); | |
186 r.start(); | |
187 } | |
188 | |
189 inline void | |
190 stop_counters(time_counter& t, resource_counter& r) | |
191 { | |
192 t.stop(); | |
193 r.stop(); | |
194 } | |
195 | |
196 inline void | |
197 clear_counters(time_counter& t, resource_counter& r) | |
198 { | |
199 t.clear(); | |
200 r.clear(); | |
201 } | |
202 | |
203 void | |
204 report_performance(const std::string file, const std::string comment, | |
205 const time_counter& t, const resource_counter& r) | |
206 { | |
207 const char space = ' '; | |
208 const char tab = '\t'; | |
209 const char* name = "libstdc++-performance.sum"; | |
210 std::string::const_iterator i = file.begin() + file.find_last_of('/') + 1; | |
211 std::string testname(i, file.end()); | |
212 | |
213 std::ofstream out(name, std::ios_base::app); | |
214 | |
215 #ifdef __GTHREADS | |
216 if (__gthread_active_p()) | |
217 testname.append("-thread"); | |
218 #endif | |
219 | |
220 out.setf(std::ios_base::left); | |
221 out << std::setw(25) << testname << tab; | |
222 out << std::setw(25) << comment << tab; | |
223 | |
224 out.setf(std::ios_base::right); | |
225 out << std::setw(4) << t.real_time() << "r" << space; | |
226 out << std::setw(4) << t.user_time() << "u" << space; | |
227 out << std::setw(4) << t.system_time() << "s" << space; | |
228 out << std::setw(8) << r.allocated_memory() << "mem" << space; | |
229 out << std::setw(4) << r.hard_page_fault() << "pf" << space; | |
230 | |
231 out << std::endl; | |
232 out.close(); | |
233 } | |
234 | |
235 void | |
236 report_header(const std::string file, const std::string header) | |
237 { | |
238 const char space = ' '; | |
239 const char tab = '\t'; | |
240 const char* name = "libstdc++-performance.sum"; | |
241 std::string::const_iterator i = file.begin() + file.find_last_of('/') + 1; | |
242 std::string testname(i, file.end()); | |
243 | |
244 std::ofstream out(name, std::ios_base::app); | |
245 | |
246 #ifdef __GTHREADS | |
247 if (__gthread_active_p ()) | |
248 testname.append("-thread"); | |
249 #endif | |
250 | |
251 out.setf(std::ios_base::left); | |
252 out << std::setw(25) << testname << tab; | |
253 out << std::setw(40) << header << tab; | |
254 | |
255 out << std::endl; | |
256 out.close(); | |
257 } | |
258 } // namespace __gnu_test | |
259 | |
260 | |
261 // Ah, we wish it wasn't so... | |
262 bool first_container = false; | |
263 extern const char* filename; | |
264 | |
265 typedef std::string::size_type (*callback_type) (std::string&); | |
266 | |
267 template<typename Container, int Iter, bool Thread> | |
268 void | |
269 write_viz_container(callback_type find_container, const char* filename) | |
270 { | |
271 typedef std::string string; | |
272 | |
273 // Create title. | |
274 { | |
275 const char ws(' '); | |
276 std::ostringstream title; | |
277 | |
278 std::string titlename(filename); | |
279 std::string::size_type n = titlename.find('.'); | |
280 if (n != string::npos) | |
281 titlename = std::string(titlename.begin(), titlename.begin() + n); | |
282 | |
283 title << titlename; | |
284 title << ws; | |
285 title << Iter; | |
286 title << ws; | |
287 #if 0 | |
288 title << "thread<"; | |
289 std::boolalpha(title); | |
290 title << Thread; | |
291 title << '>'; | |
292 #endif | |
293 | |
294 titlename += ".title"; | |
295 std::ofstream titlefile(titlename.c_str()); | |
296 if (!titlefile.good()) | |
297 throw std::runtime_error("write_viz_data cannot open titlename"); | |
298 titlefile << title.str() << std::endl; | |
299 } | |
300 | |
301 // Create compressed type name. | |
302 Container obj; | |
303 int status; | |
304 std::string type(abi::__cxa_demangle(typeid(obj).name(), 0, 0, &status)); | |
305 | |
306 // Extract fully-qualified typename. | |
307 // Assumes "set" or "map" are uniquely determinate. | |
308 string::iterator beg = type.begin(); | |
309 string::iterator end; | |
310 string::size_type n = (*find_container)(type); | |
311 | |
312 // Find start of fully-qualified name. | |
313 // Assume map, find end. | |
314 string::size_type nend = type.find('<', n); | |
315 if (nend != string::npos) | |
316 end = type.begin() + nend; | |
317 | |
318 string compressed_type; | |
319 compressed_type += '"'; | |
320 compressed_type += string(beg, end); | |
321 compressed_type += '<'; | |
322 #if 0 | |
323 typename Container::key_type v; | |
324 compressed_type += typeid(v).name(); | |
325 #else | |
326 compressed_type += "int"; | |
327 #endif | |
328 compressed_type += ", A>"; | |
329 | |
330 // XXX | |
331 if (Thread == true) | |
332 compressed_type += " thread"; | |
333 compressed_type += '"'; | |
334 | |
335 std::ofstream file(filename, std::ios_base::app); | |
336 if (!file.good()) | |
337 throw std::runtime_error("write_viz_data cannot open filename"); | |
338 | |
339 file << compressed_type; | |
340 first_container = false; | |
341 } | |
342 | |
343 | |
344 void | |
345 write_viz_data(__gnu_test::time_counter& time, const char* filename) | |
346 { | |
347 std::ofstream file(filename, std::ios_base::app); | |
348 if (!file.good()) | |
349 throw std::runtime_error("write_viz_data cannot open filename"); | |
350 | |
351 // Print out score in appropriate column. | |
352 const char tab('\t'); | |
353 int score = time.real_time(); | |
354 file << tab << score; | |
355 } | |
356 | |
357 void | |
358 write_viz_endl(const char* filename) | |
359 { | |
360 std::ofstream file(filename, std::ios_base::app); | |
361 if (!file.good()) | |
362 throw std::runtime_error("write_viz_endl cannot open filename"); | |
363 file << std::endl; | |
364 } | |
365 | |
366 | |
367 // Function template, function objects for the tests. | |
368 template<typename TestType> | |
369 struct value_type : public std::pair<const TestType, TestType> | |
370 { | |
371 inline value_type& operator++() | |
372 { | |
373 ++this->second; | |
374 return *this; | |
375 } | |
376 | |
377 inline operator TestType() const { return this->second; } | |
378 }; | |
379 | |
380 template<typename Container, int Iter> | |
381 void | |
382 do_loop(); | |
383 | |
384 template<typename Container, int Iter> | |
385 void* | |
386 do_thread(void* p = NULL) | |
387 { | |
388 do_loop<Container, Iter>(); | |
389 return p; | |
390 } | |
391 | |
392 template<typename Container, int Iter, bool Thread> | |
393 void | |
394 test_container(const char* filename) | |
395 { | |
396 using namespace __gnu_test; | |
397 time_counter time; | |
398 resource_counter resource; | |
399 { | |
400 start_counters(time, resource); | |
401 if (!Thread) | |
402 { | |
403 // No threads, so run 4x. | |
404 do_loop<Container, Iter * 4>(); | |
405 } | |
406 else | |
407 { | |
408 #if defined (_GLIBCXX_GCC_GTHR_POSIX_H) && !defined (NOTHREAD) | |
409 pthread_t t1, t2, t3, t4; | |
410 pthread_create(&t1, 0, &do_thread<Container, Iter>, 0); | |
411 pthread_create(&t2, 0, &do_thread<Container, Iter>, 0); | |
412 pthread_create(&t3, 0, &do_thread<Container, Iter>, 0); | |
413 pthread_create(&t4, 0, &do_thread<Container, Iter>, 0); | |
414 | |
415 pthread_join(t1, NULL); | |
416 pthread_join(t2, NULL); | |
417 pthread_join(t3, NULL); | |
418 pthread_join(t4, NULL); | |
419 #endif | |
420 } | |
421 stop_counters(time, resource); | |
422 | |
423 // Detailed text data. | |
424 Container obj; | |
425 int status; | |
426 std::ostringstream comment; | |
427 comment << "type: " << abi::__cxa_demangle(typeid(obj).name(), | |
428 0, 0, &status); | |
429 report_header(filename, comment.str()); | |
430 report_performance("", "", time, resource); | |
431 | |
432 // Detailed data for visualization. | |
433 std::string vizfilename(filename); | |
434 vizfilename += ".dat"; | |
435 write_viz_data(time, vizfilename.c_str()); | |
436 } | |
437 } | |
438 | |
439 template<bool Thread> | |
440 struct test_sequence | |
441 { | |
442 test_sequence(const char* filename) : _M_filename(filename) { } | |
443 | |
444 template<class Container> | |
445 void | |
446 operator()(Container) | |
447 { | |
448 const int i = 20000; | |
449 test_container<Container, i, Thread>(_M_filename); | |
450 } | |
451 | |
452 private: | |
453 const char* _M_filename; | |
454 }; | |
455 | |
456 | |
457 inline std::string::size_type | |
458 sequence_find_container(std::string& type) | |
459 { | |
460 const std::string::size_type npos = std::string::npos; | |
461 std::string::size_type n1 = type.find("vector"); | |
462 std::string::size_type n2 = type.find("list"); | |
463 std::string::size_type n3 = type.find("deque"); | |
464 std::string::size_type n4 = type.find("string"); | |
465 | |
466 if (n1 != npos || n2 != npos || n3 != npos || n4 != npos) | |
467 return std::min(std::min(n1, n2), std::min(n3, n4)); | |
468 else | |
469 throw std::runtime_error("sequence_find_container not found"); | |
470 } | |
471 | |
472 inline std::string::size_type | |
473 associative_find_container(std::string& type) | |
474 { | |
475 using std::string; | |
476 string::size_type n1 = type.find("map"); | |
477 string::size_type n2 = type.find("set"); | |
478 if (n1 != string::npos || n2 != string::npos) | |
479 return std::min(n1, n2); | |
480 else | |
481 throw std::runtime_error("associative_find_container not found"); | |
482 } | |
483 | |
484 #endif // _GLIBCXX_PERFORMANCE_H | |
485 | |
OLD | NEW |