| 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 |