xref: /oneTBB/test/common/cpu_usertime.h (revision c4568449)
1 /*
2     Copyright (c) 2005-2023 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 #ifndef __TBB_test_common_cpu_usertime_H_
18 #define __TBB_test_common_cpu_usertime_H_
19 
20 #include "config.h"
21 
22 #include "utils_report.h"
23 #include "utils.h"
24 
25 #include "oneapi/tbb/tick_count.h"
26 
27 #include <cstdio>
28 
29 #if _WIN32
30 #include <windows.h>
31 #else
32 #include <sys/time.h>
33 #include <sys/resource.h>
34 #endif
35 
36 //! Return time (in seconds) spent by the current process in user mode.
37 /*  Returns 0 if not implemented on platform. */
GetCPUUserTime()38 inline double GetCPUUserTime() {
39 #if __TBB_WIN8UI_SUPPORT
40     return 0;
41 #elif _WIN32
42     FILETIME my_times[4];
43     bool status = GetProcessTimes(GetCurrentProcess(), my_times, my_times+1, my_times+2, my_times+3)!=0;
44     CHECK_FAST(status);
45     LARGE_INTEGER usrtime;
46     usrtime.LowPart = my_times[3].dwLowDateTime;
47     usrtime.HighPart = my_times[3].dwHighDateTime;
48     return double(usrtime.QuadPart)*1E-7;
49 #else
50     // Generic UNIX, including __APPLE__
51 
52     // On Linux, there is no good way to get CPU usage info for the current process:
53     //   getrusage(RUSAGE_SELF, ...) that is used now only returns info for the calling thread;
54     //   getrusage(RUSAGE_CHILDREN, ...) only counts for finished children threads;
55     //   tms_utime and tms_cutime got with times(struct tms*) are equivalent to the above items;
56     //   finally, /proc/self/task/<task_id>/stat doesn't exist on older kernels
57     //      and it isn't quite convenient to read it for every task_id.
58 
59     struct rusage resources;
60     bool status = getrusage(RUSAGE_SELF, &resources)==0;
61     CHECK( status );
62     return (double(resources.ru_utime.tv_sec)*1E6 + double(resources.ru_utime.tv_usec))*1E-6;
63 #endif
64 }
65 
66 // The resolution of GetCPUUserTime is 10-15 ms or so; waittime should be a few times bigger.
67 const double WAITTIME = 0.1; // in seconds, i.e. 100 ms
68 const double THRESHOLD = WAITTIME/100;
69 
70 inline void TestCPUUserTime( std::size_t nthreads, std::size_t nactive = 1 ) {
71     // The test will always pass on Linux; read the comments in GetCPUUserTime for details
72     // Also it will not detect spinning issues on systems with only one processing core.
73 
74     std::size_t nworkers = nthreads-nactive;
75     if( !nworkers ) return;
76     double lastusrtime = GetCPUUserTime();
77     if( !lastusrtime ) return;
78 
79     static double minimal_waittime = WAITTIME,
80                   maximal_waittime = WAITTIME * 10;
81     double usrtime_delta;
82     double waittime_delta;
83     tbb::tick_count stamp = tbb::tick_count::now();
84     // wait for GetCPUUserTime update
85     while ( (usrtime_delta=GetCPUUserTime()-lastusrtime) < THRESHOLD ) {
86         utils::doDummyWork(1000); // do fake work without which user time can stall
87         if ( (waittime_delta = (tbb::tick_count::now()-stamp).seconds()) > maximal_waittime ) {
88             REPORT( "Warning: %.2f sec elapsed but user mode time is still below its threshold (%g < %g)\n",
89                     waittime_delta, usrtime_delta, THRESHOLD );
90             break;
91         }
92     }
93     lastusrtime += usrtime_delta;
94 
95     // Wait for workers to go sleep
96     stamp = tbb::tick_count::now();
97     while ( ((waittime_delta=(tbb::tick_count::now()-stamp).seconds()) < minimal_waittime)
98             || ((usrtime_delta=GetCPUUserTime()-lastusrtime) < THRESHOLD) )
99     {
100         utils::doDummyWork(1000); // do fake work without which user time can stall
101         if ( waittime_delta > maximal_waittime ) {
102             REPORT("Warning: %.2f sec elapsed but GetCPUUserTime reported only %g sec\n", waittime_delta, usrtime_delta );
103             break;
104         }
105     }
106 
107     // Test that all workers sleep when no work.
108     while ( nactive>1 && usrtime_delta-nactive*waittime_delta<0 ) {
109         // probably the number of active threads was mispredicted
110         --nactive; ++nworkers;
111     }
112     double avg_worker_usrtime = (usrtime_delta-nactive*waittime_delta)/nworkers;
113 
114     if ( avg_worker_usrtime > waittime_delta/2 )
115         CHECK_MESSAGE( false, "ERROR: " << nworkers << " worker threads are spinning; waittime: " <<  waittime_delta << "; usrtime: " << usrtime_delta << "; avg worker usrtime: " << avg_worker_usrtime);
116     else {
117         INFO("worker threads " << nworkers << " ; waittime: " << waittime_delta << "; usrtime: " << usrtime_delta << " ; avg worker usrtime: " << avg_worker_usrtime);
118     }
119 }
120 #endif // __TBB_test_common_cpu_usertime_H_
121