2 // Testing performance utilities for the C++ library testsuite.
4 // Copyright (C) 2003-2022 Free Software Foundation, Inc.
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)
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.
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/>.
22 #ifndef _GLIBCXX_PERFORMANCE_H
23 #define _GLIBCXX_PERFORMANCE_H
25 #include <sys/times.h>
26 #include <sys/resource.h>
36 #include <testsuite_common_types.h>
38 #if defined (__linux__) || defined (__GLIBC__)
40 #elif defined (__FreeBSD__)
52 struct mallinfo m
= { (((std::size_t) sbrk (0) + 1023) / 1024), 0 };
56 #elif !defined (__hpux__)
65 struct mallinfo empty
= { 0, 0 };
78 clock_t elapsed_begin
;
82 std::size_t splits
[3];
87 : elapsed_begin(), elapsed_end(), tms_begin(), tms_end(), splits()
93 elapsed_begin
= clock_t();
94 elapsed_end
= clock_t();
97 splits
[0] = splits
[1] = splits
[2] = 0;
104 elapsed_begin
= times(&tms_begin
);
105 const clock_t err
= clock_t(-1);
106 if (elapsed_begin
== err
)
107 std::__throw_runtime_error("time_counter::start");
113 elapsed_end
= times(&tms_end
);
114 const clock_t err
= clock_t(-1);
115 if (elapsed_end
== err
)
116 std::__throw_runtime_error("time_counter::stop");
122 splits
[0] += (elapsed_end
- elapsed_begin
);
123 splits
[1] += (tms_end
.tms_utime
- tms_begin
.tms_utime
);
124 splits
[2] += (tms_end
.tms_stime
- tms_begin
.tms_stime
);
125 elapsed_begin
= times(&tms_begin
);
126 const clock_t err
= clock_t(-1);
127 if (elapsed_begin
== err
)
128 std::__throw_runtime_error("time_counter::restart");
133 { return (elapsed_end
- elapsed_begin
) + splits
[0]; }
137 { return (tms_end
.tms_utime
- tms_begin
.tms_utime
) + splits
[1]; }
141 { return (tms_end
.tms_stime
- tms_begin
.tms_stime
) + splits
[1]; }
144 class resource_counter
149 struct mallinfo allocation_begin
;
150 struct mallinfo allocation_end
;
153 resource_counter(int i
= RUSAGE_SELF
) : who(i
)
159 memset(&rusage_begin
, 0, sizeof(rusage_begin
));
160 memset(&rusage_end
, 0, sizeof(rusage_end
));
161 memset(&allocation_begin
, 0, sizeof(allocation_begin
));
162 memset(&allocation_end
, 0, sizeof(allocation_end
));
168 if (getrusage(who
, &rusage_begin
) != 0 )
169 memset(&rusage_begin
, 0, sizeof(rusage_begin
));
170 void* p
__attribute__((unused
)) = malloc(0); // Needed for some implementations.
171 allocation_begin
= mallinfo();
177 if (getrusage(who
, &rusage_end
) != 0 )
178 memset(&rusage_end
, 0, sizeof(rusage_end
));
179 allocation_end
= mallinfo();
183 allocated_memory() const
184 { return ((allocation_end
.uordblks
- allocation_begin
.uordblks
)
185 + (allocation_end
.hblkhd
- allocation_begin
.hblkhd
)); }
188 hard_page_fault() const
189 { return rusage_end
.ru_majflt
- rusage_begin
.ru_majflt
; }
193 { return rusage_end
.ru_nswap
- rusage_begin
.ru_nswap
; }
197 start_counters(time_counter
& t
, resource_counter
& r
)
204 stop_counters(time_counter
& t
, resource_counter
& r
)
211 clear_counters(time_counter
& t
, resource_counter
& r
)
218 report_performance(const std::string file
, const std::string comment
,
219 const time_counter
& t
, const resource_counter
& r
)
221 const char space
= ' ';
222 const char tab
= '\t';
223 const char* name
= "libstdc++-performance.sum";
224 std::string::const_iterator i
= file
.begin() + file
.find_last_of('/') + 1;
225 std::string
testname(i
, file
.end());
227 std::ofstream
out(name
, std::ios_base::app
);
230 if (__gthread_active_p())
231 testname
.append("-thread");
234 out
.setf(std::ios_base::left
);
235 out
<< std::setw(25) << testname
<< tab
;
236 out
<< std::setw(25) << comment
<< tab
;
238 out
.setf(std::ios_base::right
);
239 out
<< std::setw(4) << t
.real_time() << "r" << space
;
240 out
<< std::setw(4) << t
.user_time() << "u" << space
;
241 out
<< std::setw(4) << t
.system_time() << "s" << space
;
242 out
<< std::setw(8) << r
.allocated_memory() << "mem" << space
;
243 out
<< std::setw(4) << r
.hard_page_fault() << "pf" << space
;
250 report_header(const std::string file
, const std::string header
)
252 const char tab
= '\t';
253 const char* name
= "libstdc++-performance.sum";
254 std::string::const_iterator i
= file
.begin() + file
.find_last_of('/') + 1;
255 std::string
testname(i
, file
.end());
257 std::ofstream
out(name
, std::ios_base::app
);
260 if (__gthread_active_p ())
261 testname
.append("-thread");
264 out
.setf(std::ios_base::left
);
265 out
<< std::setw(25) << testname
<< tab
;
266 out
<< std::setw(40) << header
<< tab
;
271 } // namespace __gnu_test
274 // Ah, we wish it wasn't so...
275 bool first_container
= false;
276 extern const char* filename
;
278 typedef std::string::size_type (*callback_type
) (std::string
&);
280 template<typename Container
, int Iter
, bool Thread
>
282 write_viz_container(callback_type find_container
, const char* filename
)
284 typedef std::string string
;
289 std::ostringstream title
;
291 std::string
titlename(filename
);
292 std::string::size_type n
= titlename
.find('.');
293 if (n
!= string::npos
)
294 titlename
= std::string(titlename
.begin(), titlename
.begin() + n
);
302 std::boolalpha(title
);
307 titlename
+= ".title";
308 std::ofstream
titlefile(titlename
.c_str());
309 if (!titlefile
.good())
310 throw std::runtime_error("write_viz_data cannot open titlename");
311 titlefile
<< title
.str() << std::endl
;
314 // Create compressed type name.
317 std::string
type(abi::__cxa_demangle(typeid(obj
).name(), 0, 0, &status
));
319 // Extract fully-qualified typename.
320 // Assumes "set" or "map" are uniquely determinate.
321 string::iterator beg
= type
.begin();
322 string::iterator end
;
323 string::size_type n
= (*find_container
)(type
);
325 // Find start of fully-qualified name.
326 // Assume map, find end.
327 string::size_type nend
= type
.find('<', n
);
328 if (nend
!= string::npos
)
329 end
= type
.begin() + nend
;
331 string compressed_type
;
332 compressed_type
+= '"';
333 compressed_type
+= string(beg
, end
);
334 compressed_type
+= '<';
336 typename
Container::key_type v
;
337 compressed_type
+= typeid(v
).name();
339 compressed_type
+= "int";
341 compressed_type
+= ", A>";
345 compressed_type
+= " thread";
346 compressed_type
+= '"';
348 std::ofstream
file(filename
, std::ios_base::app
);
350 throw std::runtime_error("write_viz_data cannot open filename");
352 file
<< compressed_type
;
353 first_container
= false;
358 write_viz_data(__gnu_test::time_counter
& time
, const char* filename
)
360 std::ofstream
file(filename
, std::ios_base::app
);
362 throw std::runtime_error("write_viz_data cannot open filename");
364 // Print out score in appropriate column.
365 const char tab('\t');
366 int score
= time
.real_time();
367 file
<< tab
<< score
;
371 write_viz_endl(const char* filename
)
373 std::ofstream
file(filename
, std::ios_base::app
);
375 throw std::runtime_error("write_viz_endl cannot open filename");
380 // Function template, function objects for the tests.
381 template<typename TestType
>
382 struct value_type
: public std::pair
<const TestType
, TestType
>
384 inline value_type
& operator++()
390 inline operator TestType() const { return this->second
; }
393 template<typename Container
, int Iter
>
397 template<typename Container
, int Iter
>
399 do_thread(void* p
= 0)
401 do_loop
<Container
, Iter
>();
405 template<typename Container
, int Iter
, bool Thread
>
407 test_container(const char* filename
)
409 using namespace __gnu_test
;
411 resource_counter resource
;
413 start_counters(time
, resource
);
416 // No threads, so run 4x.
417 do_loop
<Container
, Iter
* 4>();
421 #if defined (_GLIBCXX_GCC_GTHR_POSIX_H) && !defined (NOTHREAD)
422 pthread_t t1
, t2
, t3
, t4
;
423 pthread_create(&t1
, 0, &do_thread
<Container
, Iter
>, 0);
424 pthread_create(&t2
, 0, &do_thread
<Container
, Iter
>, 0);
425 pthread_create(&t3
, 0, &do_thread
<Container
, Iter
>, 0);
426 pthread_create(&t4
, 0, &do_thread
<Container
, Iter
>, 0);
434 stop_counters(time
, resource
);
436 // Detailed text data.
439 std::ostringstream comment
;
440 comment
<< "type: " << abi::__cxa_demangle(typeid(obj
).name(),
442 report_header(filename
, comment
.str());
443 report_performance("", "", time
, resource
);
445 // Detailed data for visualization.
446 std::string
vizfilename(filename
);
447 vizfilename
+= ".dat";
448 write_viz_data(time
, vizfilename
.c_str());
452 template<bool Thread
>
455 test_sequence(const char* filename
) : _M_filename(filename
) { }
457 template<class Container
>
459 operator()(Container
)
462 test_container
<Container
, i
, Thread
>(_M_filename
);
466 const char* _M_filename
;
470 inline std::string::size_type
471 sequence_find_container(std::string
& type
)
473 const std::string::size_type npos
= std::string::npos
;
474 std::string::size_type n1
= type
.find("vector");
475 std::string::size_type n2
= type
.find("list");
476 std::string::size_type n3
= type
.find("deque");
477 std::string::size_type n4
= type
.find("string");
479 if (n1
!= npos
|| n2
!= npos
|| n3
!= npos
|| n4
!= npos
)
480 return std::min(std::min(n1
, n2
), std::min(n3
, n4
));
482 throw std::runtime_error("sequence_find_container not found");
485 inline std::string::size_type
486 associative_find_container(std::string
& type
)
489 string::size_type n1
= type
.find("map");
490 string::size_type n2
= type
.find("set");
491 if (n1
!= string::npos
|| n2
!= string::npos
)
492 return std::min(n1
, n2
);
494 throw std::runtime_error("associative_find_container not found");
497 #endif // _GLIBCXX_PERFORMANCE_H