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. */ |
30 | static 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. |
62 | const double WAITTIME = 0.1; // in seconds, i.e. 100 ms |
63 | const double THRESHOLD = WAITTIME/100; |
64 | |
65 | static 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 | |