| 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 | |