1/*
2 Copyright (c) 2005-2019 Intel Corporation
3
4 Licensed under the Apache License, Version 2.0 (the "License");
5 you may not use this file except in compliance with the License.
6 You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10 Unless required by applicable law or agreed to in writing, software
11 distributed under the License is distributed on an "AS IS" BASIS,
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 See the License for the specific language governing permissions and
14 limitations under the License.
15*/
16
17// Declarations for simple estimate of CPU time being used by a program.
18// This header is an optional part of the test harness.
19// It assumes that "harness_assert.h" has already been included.
20
21#if _WIN32
22 #include <windows.h>
23#else
24 #include <sys/time.h>
25 #include <sys/resource.h>
26#endif
27
28//! Return time (in seconds) spent by the current process in user mode.
29/* Returns 0 if not implemented on platform. */
30static double GetCPUUserTime() {
31#if __TBB_WIN8UI_SUPPORT
32 return 0;
33#elif _WIN32
34 FILETIME my_times[4];
35 bool status = GetProcessTimes(GetCurrentProcess(), my_times, my_times+1, my_times+2, my_times+3)!=0;
36 ASSERT( status, NULL );
37 LARGE_INTEGER usrtime;
38 usrtime.LowPart = my_times[3].dwLowDateTime;
39 usrtime.HighPart = my_times[3].dwHighDateTime;
40 return double(usrtime.QuadPart)*1E-7;
41#else
42 // Generic UNIX, including __APPLE__
43
44 // On Linux, there is no good way to get CPU usage info for the current process:
45 // getrusage(RUSAGE_SELF, ...) that is used now only returns info for the calling thread;
46 // getrusage(RUSAGE_CHILDREN, ...) only counts for finished children threads;
47 // tms_utime and tms_cutime got with times(struct tms*) are equivalent to the above items;
48 // finally, /proc/self/task/<task_id>/stat doesn't exist on older kernels
49 // and it isn't quite convenient to read it for every task_id.
50
51 struct rusage resources;
52 bool status = getrusage(RUSAGE_SELF, &resources)==0;
53 ASSERT( status, NULL );
54 return (double(resources.ru_utime.tv_sec)*1E6 + double(resources.ru_utime.tv_usec))*1E-6;
55#endif
56}
57
58#include "tbb/tick_count.h"
59#include <cstdio>
60
61// The resolution of GetCPUUserTime is 10-15 ms or so; waittime should be a few times bigger.
62const double WAITTIME = 0.1; // in seconds, i.e. 100 ms
63const double THRESHOLD = WAITTIME/100;
64
65static void TestCPUUserTime( int nthreads, int nactive = 1 ) {
66 // The test will always pass on Linux; read the comments in GetCPUUserTime for details
67 // Also it will not detect spinning issues on systems with only one processing core.
68
69 int nworkers = nthreads-nactive;
70 if( !nworkers ) return;
71 double lastusrtime = GetCPUUserTime();
72 if( !lastusrtime ) return;
73
74 static double minimal_waittime = WAITTIME,
75 maximal_waittime = WAITTIME * 10;
76 double usrtime_delta;
77 double waittime_delta;
78 tbb::tick_count stamp = tbb::tick_count::now();
79 volatile intptr_t k = (intptr_t)&usrtime_delta;
80 // wait for GetCPUUserTime update
81 while( (usrtime_delta=GetCPUUserTime()-lastusrtime) < THRESHOLD ) {
82 for ( int i = 0; i < 1000; ++i ) ++k; // do fake work without which user time can stall
83 if ( (waittime_delta = (tbb::tick_count::now()-stamp).seconds()) > maximal_waittime ) {
84 REPORT( "Warning: %.2f sec elapsed but user mode time is still below its threshold (%g < %g)\n",
85 waittime_delta, usrtime_delta, THRESHOLD );
86 break;
87 }
88 }
89 lastusrtime += usrtime_delta;
90
91 // Wait for workers to go sleep
92 stamp = tbb::tick_count::now();
93 while( ((waittime_delta=(tbb::tick_count::now()-stamp).seconds()) < minimal_waittime)
94 || ((usrtime_delta=GetCPUUserTime()-lastusrtime) < THRESHOLD) )
95 {
96 for ( int i = 0; i < 1000; ++i ) ++k; // do fake work without which user time can stall
97 if ( waittime_delta > maximal_waittime ) {
98 REPORT( "Warning: %.2f sec elapsed but GetCPUUserTime reported only %g sec\n", waittime_delta, usrtime_delta );
99 break;
100 }
101 }
102
103 // Test that all workers sleep when no work.
104 while( nactive>1 && usrtime_delta-nactive*waittime_delta<0 ) {
105 // probably the number of active threads was mispredicted
106 --nactive; ++nworkers;
107 }
108 double avg_worker_usrtime = (usrtime_delta-nactive*waittime_delta)/nworkers;
109
110 if( avg_worker_usrtime > waittime_delta/2 )
111 REPORT( "ERROR: %d worker threads are spinning; waittime: %g; usrtime: %g; avg worker usrtime: %g\n",
112 nworkers, waittime_delta, usrtime_delta, avg_worker_usrtime);
113 else
114 REMARK("%d worker threads; waittime: %g; usrtime: %g; avg worker usrtime: %g\n",
115 nworkers, waittime_delta, usrtime_delta, avg_worker_usrtime);
116}
117