| 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 | #include "tbb/tick_count.h" | 
|---|
| 18 | #include "harness_assert.h" | 
|---|
| 19 |  | 
|---|
| 20 | //! Assert that two times in seconds are very close. | 
|---|
| 21 | void AssertNear( double x, double y ) { | 
|---|
| 22 | ASSERT( -1.0E-10 <= x-y && x-y <=1.0E-10, NULL ); | 
|---|
| 23 | } | 
|---|
| 24 |  | 
|---|
| 25 | //! Test arithmetic operators on tick_count::interval_t | 
|---|
| 26 | void TestArithmetic( const tbb::tick_count& t0, const tbb::tick_count& t1, const tbb::tick_count& t2 ) { | 
|---|
| 27 | tbb::tick_count::interval_t i= t1-t0; | 
|---|
| 28 | tbb::tick_count::interval_t j = t2-t1; | 
|---|
| 29 | tbb::tick_count::interval_t k = t2-t0; | 
|---|
| 30 | AssertSameType( tbb::tick_count::interval_t(), i-j ); | 
|---|
| 31 | AssertSameType( tbb::tick_count::interval_t(), i+j ); | 
|---|
| 32 | ASSERT( i.seconds()>1E-9, NULL ); | 
|---|
| 33 | ASSERT( j.seconds()>1E-9, NULL ); | 
|---|
| 34 | ASSERT( k.seconds()>2E-9, NULL ); | 
|---|
| 35 | AssertNear( (i+j).seconds(), k.seconds() ); | 
|---|
| 36 | AssertNear( (k-j).seconds(), i.seconds() ); | 
|---|
| 37 | AssertNear( ((k-j)+(j-i)).seconds(), k.seconds()-i.seconds() ); | 
|---|
| 38 | tbb::tick_count::interval_t sum; | 
|---|
| 39 | sum += i; | 
|---|
| 40 | sum += j; | 
|---|
| 41 | AssertNear( sum.seconds(), k.seconds() ); | 
|---|
| 42 | sum -= i; | 
|---|
| 43 | AssertNear( sum.seconds(), j.seconds() ); | 
|---|
| 44 | sum -= j; | 
|---|
| 45 | AssertNear( sum.seconds(), 0.0 ); | 
|---|
| 46 | } | 
|---|
| 47 |  | 
|---|
| 48 | //------------------------------------------------------------------------ | 
|---|
| 49 | // Test for overhead in calls to tick_count | 
|---|
| 50 | //------------------------------------------------------------------------ | 
|---|
| 51 |  | 
|---|
| 52 | //! Wait for given duration. | 
|---|
| 53 | /** The duration parameter is in units of seconds. */ | 
|---|
| 54 | static void WaitForDuration( double duration ) { | 
|---|
| 55 | tbb::tick_count start = tbb::tick_count::now(); | 
|---|
| 56 | while( (tbb::tick_count::now()-start).seconds() < duration ) | 
|---|
| 57 | continue; | 
|---|
| 58 | } | 
|---|
| 59 |  | 
|---|
| 60 | #include "harness.h" | 
|---|
| 61 |  | 
|---|
| 62 | //! Test that average timer overhead is within acceptable limit. | 
|---|
| 63 | /** The 'tolerance' value inside the test specifies the limit. */ | 
|---|
| 64 | void TestSimpleDelay( int ntrial, double duration, double tolerance ) { | 
|---|
| 65 | int error_count = 0; | 
|---|
| 66 | double delta = 0; | 
|---|
| 67 | // Iteration -1 warms up the code cache. | 
|---|
| 68 | for( int trial=-1; trial<ntrial; ++trial ) { | 
|---|
| 69 | tbb::tick_count t = tbb::tick_count::now(); | 
|---|
| 70 | if( duration ) WaitForDuration(duration); | 
|---|
| 71 | delta = (tbb::tick_count::now() - t).seconds() - duration; | 
|---|
| 72 | if( trial>=0 && delta > tolerance ) { | 
|---|
| 73 | error_count++; | 
|---|
| 74 | } | 
|---|
| 75 | ASSERT(delta >= 0, "Delta is negative"); | 
|---|
| 76 | } | 
|---|
| 77 | ASSERT(error_count < ntrial / 4, "The number of errors exceeded the threshold"); | 
|---|
| 78 | } | 
|---|
| 79 |  | 
|---|
| 80 | //------------------------------------------------------------------------ | 
|---|
| 81 | // Test for subtracting calls to tick_count from different threads. | 
|---|
| 82 | //------------------------------------------------------------------------ | 
|---|
| 83 |  | 
|---|
| 84 | #include "tbb/atomic.h" | 
|---|
| 85 | static tbb::atomic<int> Counter1, Counter2; | 
|---|
| 86 | static tbb::atomic<bool> Flag1, Flag2; | 
|---|
| 87 | static tbb::tick_count *tick_count_array; | 
|---|
| 88 | static double barrier_time; | 
|---|
| 89 |  | 
|---|
| 90 | struct TickCountDifferenceBody { | 
|---|
| 91 | TickCountDifferenceBody( int num_threads ) { | 
|---|
| 92 | Counter1 = Counter2 = num_threads; | 
|---|
| 93 | Flag1 = Flag2 = false; | 
|---|
| 94 | } | 
|---|
| 95 | void operator()( int id ) const { | 
|---|
| 96 | bool last = false; | 
|---|
| 97 | // The first barrier. | 
|---|
| 98 | if ( --Counter1 == 0 ) last = true; | 
|---|
| 99 | while ( !last && !Flag1.load<tbb::acquire>() ) __TBB_Pause( 1 ); | 
|---|
| 100 | // Save a time stamp of the first barrier releasing. | 
|---|
| 101 | tick_count_array[id] = tbb::tick_count::now(); | 
|---|
| 102 |  | 
|---|
| 103 | // The second barrier. | 
|---|
| 104 | if ( --Counter2 == 0 ) Flag2.store<tbb::release>(true); | 
|---|
| 105 | // The last thread should release threads from the first barrier after it reaches the second | 
|---|
| 106 | // barrier to avoid a deadlock. | 
|---|
| 107 | if ( last ) Flag1.store<tbb::release>(true); | 
|---|
| 108 | // After the last thread releases threads from the first barrier it waits for a signal from | 
|---|
| 109 | // the second barrier. | 
|---|
| 110 | while ( !Flag2.load<tbb::acquire>() ) __TBB_Pause( 1 ); | 
|---|
| 111 |  | 
|---|
| 112 | if ( last ) | 
|---|
| 113 | // We suppose that the barrier time is a time interval between the moment when the last | 
|---|
| 114 | // thread reaches the first barrier and the moment when the same thread is released from | 
|---|
| 115 | // the second barrier. This time is not accurate time of two barriers but it is | 
|---|
| 116 | // guaranteed that it does not exceed it. | 
|---|
| 117 | barrier_time = (tbb::tick_count::now() - tick_count_array[id]).seconds() / 2; | 
|---|
| 118 | } | 
|---|
| 119 | ~TickCountDifferenceBody() { | 
|---|
| 120 | ASSERT( Counter1 == 0 && Counter2 == 0, NULL ); | 
|---|
| 121 | } | 
|---|
| 122 | }; | 
|---|
| 123 |  | 
|---|
| 124 | //! Test that two tick_count values recorded on different threads can be meaningfully subtracted. | 
|---|
| 125 | void TestTickCountDifference( int n ) { | 
|---|
| 126 | const double tolerance = 3E-4; | 
|---|
| 127 | tick_count_array = new tbb::tick_count[n]; | 
|---|
| 128 |  | 
|---|
| 129 | int num_trials = 0; | 
|---|
| 130 | tbb::tick_count start_time = tbb::tick_count::now(); | 
|---|
| 131 | do { | 
|---|
| 132 | NativeParallelFor( n, TickCountDifferenceBody( n ) ); | 
|---|
| 133 | if ( barrier_time > tolerance ) | 
|---|
| 134 | // The machine seems to be oversubscribed so skip the test. | 
|---|
| 135 | continue; | 
|---|
| 136 | for ( int i = 0; i < n; ++i ) { | 
|---|
| 137 | for ( int j = 0; j < i; ++j ) { | 
|---|
| 138 | double diff = (tick_count_array[i] - tick_count_array[j]).seconds(); | 
|---|
| 139 | if ( diff < 0 ) diff = -diff; | 
|---|
| 140 | if ( diff > tolerance ) | 
|---|
| 141 | REPORT( "Warning: cross-thread tick_count difference = %g > %g = tolerance\n", diff, tolerance ); | 
|---|
| 142 | ASSERT( diff < 3 * tolerance, "Too big difference."); | 
|---|
| 143 | } | 
|---|
| 144 | } | 
|---|
| 145 | // During 5 seconds we are trying to get 10 successful trials. | 
|---|
| 146 | } while ( ++num_trials < 10 && (tbb::tick_count::now() - start_time).seconds() < 5 ); | 
|---|
| 147 | REMARK( "Difference test time: %g sec\n", (tbb::tick_count::now() - start_time).seconds() ); | 
|---|
| 148 | // TODO: Find the cause of the machine high load, fix it and upgrade ASSERT_WARNING to ASSERT | 
|---|
| 149 | ASSERT_WARNING( num_trials == 10, "The machine seems to be heavily oversubscribed, difference test was skipped."); | 
|---|
| 150 | delete[] tick_count_array; | 
|---|
| 151 | } | 
|---|
| 152 |  | 
|---|
| 153 | void TestResolution() { | 
|---|
| 154 | static double target_value = 0.314159265358979323846264338327950288419; | 
|---|
| 155 | static double step_value = 0.00027182818284590452353602874713526624977572; | 
|---|
| 156 | static int range_value = 100; | 
|---|
| 157 | double avg_diff = 0.0; | 
|---|
| 158 | double max_diff = 0.0; | 
|---|
| 159 | for( int i = -range_value; i <= range_value; ++i ) { | 
|---|
| 160 | double my_time = target_value + step_value * i; | 
|---|
| 161 | tbb::tick_count::interval_t t0(my_time); | 
|---|
| 162 | double interval_time = t0.seconds(); | 
|---|
| 163 | avg_diff += (my_time - interval_time); | 
|---|
| 164 | if ( max_diff < my_time-interval_time) max_diff = my_time-interval_time; | 
|---|
| 165 | // time always truncates | 
|---|
| 166 | ASSERT(interval_time >= 0 && my_time - interval_time < tbb::tick_count::resolution(), "tick_count resolution out of range"); | 
|---|
| 167 | } | 
|---|
| 168 | avg_diff = (avg_diff/(2*range_value+1))/tbb::tick_count::resolution(); | 
|---|
| 169 | max_diff /= tbb::tick_count::resolution(); | 
|---|
| 170 | REMARK( "avg_diff = %g ticks, max_diff = %g ticks\n", avg_diff, max_diff); | 
|---|
| 171 | } | 
|---|
| 172 |  | 
|---|
| 173 | #include "tbb/tbb_thread.h" | 
|---|
| 174 |  | 
|---|
| 175 | int TestMain () { | 
|---|
| 176 | // Increased tolerance for Virtual Machines | 
|---|
| 177 | double tolerance_multiplier = Harness::GetEnv( "VIRTUAL_MACHINE") ? 50. : 1.; | 
|---|
| 178 | REMARK( "tolerance_multiplier = %g \n", tolerance_multiplier ); | 
|---|
| 179 |  | 
|---|
| 180 | tbb::tick_count t0 = tbb::tick_count::now(); | 
|---|
| 181 | TestSimpleDelay(/*ntrial=*/1000000,/*duration=*/0,    /*tolerance=*/6E-6 * tolerance_multiplier); | 
|---|
| 182 | tbb::tick_count t1 = tbb::tick_count::now(); | 
|---|
| 183 | TestSimpleDelay(/*ntrial=*/1000,   /*duration=*/0.001,/*tolerance=*/15E-6 * tolerance_multiplier); | 
|---|
| 184 | tbb::tick_count t2 = tbb::tick_count::now(); | 
|---|
| 185 | TestArithmetic(t0,t1,t2); | 
|---|
| 186 |  | 
|---|
| 187 | TestResolution(); | 
|---|
| 188 |  | 
|---|
| 189 | int num_threads = tbb::tbb_thread::hardware_concurrency(); | 
|---|
| 190 | ASSERT( num_threads > 0, "tbb::thread::hardware_concurrency() has returned an incorrect value"); | 
|---|
| 191 | if ( num_threads > 1 ) { | 
|---|
| 192 | REMARK( "num_threads = %d\n", num_threads ); | 
|---|
| 193 | TestTickCountDifference( num_threads ); | 
|---|
| 194 | } else { | 
|---|
| 195 | REPORT( "Warning: concurrency is too low for TestTickCountDifference ( num_threads = %d )\n", num_threads ); | 
|---|
| 196 | } | 
|---|
| 197 |  | 
|---|
| 198 | return Harness::Done; | 
|---|
| 199 | } | 
|---|
| 200 |  | 
|---|