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