1 // -*- C++ -*-
2 // Testing performance utilities for the C++ library testsuite.
3 //
4 // Copyright (C) 2003-2021 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__)
41 extern "C"
42 {
43   struct mallinfo
44   {
45     int uordblks;
46     int hblkhd;
47   };
48 
49   struct mallinfo
mallinfo(void)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__)
57 extern "C"
58 {
59   struct mallinfo
60   {
61     int uordblks;
62     int hblkhd;
63   };
64 
65   struct mallinfo empty = { 0, 0 };
66 
67   struct mallinfo
mallinfo(void)68   mallinfo(void)
69   { return empty; }
70 }
71 #endif
72 
73 namespace __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     std::size_t splits[3];
83 
84   public:
85     explicit
time_counter()86     time_counter()
87     : elapsed_begin(), elapsed_end(), tms_begin(), tms_end(), splits()
88     { }
89 
90     void
clear()91     clear() throw()
92     {
93       elapsed_begin = clock_t();
94       elapsed_end = clock_t();
95       tms_begin = tms();
96       tms_end = tms();
97       splits[0] = splits[1] = splits[2] = 0;
98     }
99 
100     void
start()101     start()
102     {
103       this->clear();
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");
108     }
109 
110     void
stop()111     stop()
112     {
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");
117     }
118 
119     void
restart()120     restart()
121     {
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");
129     }
130 
131     std::size_t
real_time()132     real_time() const
133     { return (elapsed_end - elapsed_begin) + splits[0]; }
134 
135     std::size_t
user_time()136     user_time() const
137     { return (tms_end.tms_utime - tms_begin.tms_utime) + splits[1]; }
138 
139     std::size_t
system_time()140     system_time() const
141     { return (tms_end.tms_stime - tms_begin.tms_stime) + splits[1]; }
142   };
143 
144   class resource_counter
145   {
146     int                 who;
147     rusage	        rusage_begin;
148     rusage	        rusage_end;
149     struct mallinfo  	allocation_begin;
150     struct mallinfo  	allocation_end;
151 
152   public:
who(i)153     resource_counter(int i = RUSAGE_SELF) : who(i)
154     { this->clear(); }
155 
156     void
clear()157     clear() throw()
158     {
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));
163     }
164 
165     void
start()166     start()
167     {
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();
172     }
173 
174     void
stop()175     stop()
176     {
177       if (getrusage(who, &rusage_end) != 0 )
178 	memset(&rusage_end, 0, sizeof(rusage_end));
179       allocation_end = mallinfo();
180     }
181 
182     int
allocated_memory()183     allocated_memory() const
184     { return ((allocation_end.uordblks - allocation_begin.uordblks)
185 	      + (allocation_end.hblkhd - allocation_begin.hblkhd)); }
186 
187     long
hard_page_fault()188     hard_page_fault() const
189     { return rusage_end.ru_majflt - rusage_begin.ru_majflt; }
190 
191     long
swapped()192     swapped() const
193     { return rusage_end.ru_nswap - rusage_begin.ru_nswap; }
194   };
195 
196   inline void
start_counters(time_counter & t,resource_counter & r)197   start_counters(time_counter& t, resource_counter& r)
198   {
199     t.start();
200     r.start();
201   }
202 
203   inline void
stop_counters(time_counter & t,resource_counter & r)204   stop_counters(time_counter& t, resource_counter& r)
205   {
206     t.stop();
207     r.stop();
208   }
209 
210   inline void
clear_counters(time_counter & t,resource_counter & r)211   clear_counters(time_counter& t, resource_counter& r)
212   {
213     t.clear();
214     r.clear();
215   }
216 
217   void
report_performance(const std::string file,const std::string comment,const time_counter & t,const resource_counter & r)218   report_performance(const std::string file, const std::string comment,
219 		     const time_counter& t, const resource_counter& r)
220   {
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());
226 
227     std::ofstream out(name, std::ios_base::app);
228 
229 #ifdef __GTHREADS
230     if (__gthread_active_p())
231       testname.append("-thread");
232 #endif
233 
234     out.setf(std::ios_base::left);
235     out << std::setw(25) << testname << tab;
236     out << std::setw(25) << comment << tab;
237 
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;
244 
245     out << std::endl;
246     out.close();
247   }
248 
249   void
report_header(const std::string file,const std::string header)250   report_header(const std::string file, const std::string header)
251   {
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());
256 
257     std::ofstream out(name, std::ios_base::app);
258 
259 #ifdef __GTHREADS
260     if (__gthread_active_p ())
261       testname.append("-thread");
262 #endif
263 
264     out.setf(std::ios_base::left);
265     out << std::setw(25) << testname << tab;
266     out << std::setw(40) << header << tab;
267 
268     out << std::endl;
269     out.close();
270   }
271 } // namespace __gnu_test
272 
273 
274 // Ah, we wish it wasn't so...
275 bool first_container = false;
276 extern const char* filename;
277 
278 typedef std::string::size_type (*callback_type) (std::string&);
279 
280 template<typename Container, int Iter, bool Thread>
281   void
write_viz_container(callback_type find_container,const char * filename)282   write_viz_container(callback_type find_container, const char* filename)
283   {
284     typedef std::string string;
285 
286     // Create title.
287     {
288       const char ws(' ');
289       std::ostringstream title;
290 
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);
295 
296       title << titlename;
297       title << ws;
298       title << Iter;
299       title << ws;
300 #if 0
301       title << "thread<";
302       std::boolalpha(title);
303       title << Thread;
304       title << '>';
305 #endif
306 
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;
312     }
313 
314     // Create compressed type name.
315     Container obj;
316     int status;
317     std::string type(abi::__cxa_demangle(typeid(obj).name(), 0, 0, &status));
318 
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);
324 
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;
330 
331     string compressed_type;
332     compressed_type += '"';
333     compressed_type += string(beg, end);
334     compressed_type += '<';
335 #if 0
336     typename Container::key_type v;
337     compressed_type += typeid(v).name();
338 #else
339     compressed_type += "int";
340 #endif
341     compressed_type += ", A>";
342 
343     // XXX
344     if (Thread == true)
345       compressed_type += " thread";
346     compressed_type += '"';
347 
348     std::ofstream file(filename, std::ios_base::app);
349     if (!file.good())
350       throw std::runtime_error("write_viz_data cannot open filename");
351 
352     file << compressed_type;
353     first_container = false;
354   }
355 
356 
357 void
write_viz_data(__gnu_test::time_counter & time,const char * filename)358 write_viz_data(__gnu_test::time_counter& time, const char* filename)
359 {
360   std::ofstream file(filename, std::ios_base::app);
361   if (!file.good())
362     throw std::runtime_error("write_viz_data cannot open filename");
363 
364   // Print out score in appropriate column.
365   const char tab('\t');
366   int score = time.real_time();
367   file << tab << score;
368 }
369 
370 void
write_viz_endl(const char * filename)371 write_viz_endl(const char* filename)
372 {
373   std::ofstream file(filename, std::ios_base::app);
374   if (!file.good())
375     throw std::runtime_error("write_viz_endl cannot open filename");
376   file << std::endl;
377 }
378 
379 
380 // Function template, function objects for the tests.
381 template<typename TestType>
382   struct value_type : public std::pair<const TestType, TestType>
383   {
384     inline value_type& operator++()
385     {
386       ++this->second;
387       return *this;
388     }
389 
TestTypevalue_type390     inline operator TestType() const { return this->second; }
391   };
392 
393 template<typename Container, int Iter>
394   void
395   do_loop();
396 
397 template<typename Container, int Iter>
398   void*
399   do_thread(void* p = 0)
400   {
401     do_loop<Container, Iter>();
402     return p;
403   }
404 
405 template<typename Container, int Iter, bool Thread>
406   void
test_container(const char * filename)407   test_container(const char* filename)
408   {
409     using namespace __gnu_test;
410     time_counter time;
411     resource_counter resource;
412     {
413       start_counters(time, resource);
414       if (!Thread)
415 	{
416 	  // No threads, so run 4x.
417 	  do_loop<Container, Iter * 4>();
418 	}
419       else
420 	{
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);
427 
428 	  pthread_join(t1, 0);
429 	  pthread_join(t2, 0);
430 	  pthread_join(t3, 0);
431 	  pthread_join(t4, 0);
432 #endif
433 	}
434       stop_counters(time, resource);
435 
436       // Detailed text data.
437       Container obj;
438       int status;
439       std::ostringstream comment;
440       comment << "type: " << abi::__cxa_demangle(typeid(obj).name(),
441                                                  0, 0, &status);
442       report_header(filename, comment.str());
443       report_performance("", "", time, resource);
444 
445       // Detailed data for visualization.
446       std::string vizfilename(filename);
447       vizfilename += ".dat";
448       write_viz_data(time, vizfilename.c_str());
449     }
450   }
451 
452 template<bool Thread>
453   struct test_sequence
454   {
test_sequencetest_sequence455     test_sequence(const char* filename) : _M_filename(filename) { }
456 
457     template<class Container>
458       void
operatortest_sequence459       operator()(Container)
460       {
461 	const int i = 20000;
462 	test_container<Container, i, Thread>(_M_filename);
463       }
464 
465   private:
466     const char* _M_filename;
467   };
468 
469 
470 inline std::string::size_type
sequence_find_container(std::string & type)471 sequence_find_container(std::string& type)
472 {
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");
478 
479   if (n1 != npos || n2 != npos || n3 != npos || n4 != npos)
480     return std::min(std::min(n1, n2), std::min(n3, n4));
481   else
482     throw std::runtime_error("sequence_find_container not found");
483 }
484 
485 inline std::string::size_type
associative_find_container(std::string & type)486 associative_find_container(std::string& type)
487 {
488   using std::string;
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);
493   else
494     throw std::runtime_error("associative_find_container not found");
495 }
496 
497 #endif // _GLIBCXX_PERFORMANCE_H
498 
499