1/*-------------------------------------------------------------------------
2 * drawElements Quality Program Tester Core
3 * ----------------------------------------
4 *
5 * Copyright 2014 The Android Open Source Project
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 *      http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 *
19 *//*!
20 * \file
21 * \brief CPU warm-up utility, used to counteract CPU throttling.
22 *//*--------------------------------------------------------------------*/
23
24#include "tcuCPUWarmup.hpp"
25#include "deDefs.hpp"
26#include "deMath.h"
27#include "deClock.h"
28
29#include <algorithm>
30
31namespace tcu
32{
33
34namespace warmupCPUInternal
35{
36
37volatile Dummy g_dummy;
38
39};
40
41template <typename T, int Size>
42static inline float floatMedian (const T (&v)[Size])
43{
44	T temp[Size];
45	for (int i = 0; i < Size; i++)
46		temp[i] = v[i];
47
48	std::sort(DE_ARRAY_BEGIN(temp), DE_ARRAY_END(temp));
49
50	return Size % 2 == 0
51		   ? 0.5f * ((float)temp[Size/2-1] + (float)temp[Size/2])
52		   : (float)temp[Size/2];
53}
54
55template <typename T, int Size>
56static inline float floatRelativeMedianAbsoluteDeviation (const T (&v)[Size])
57{
58	const float		median = floatMedian(v);
59	float			absoluteDeviations[Size];
60
61	for (int i = 0; i < Size; i++)
62		absoluteDeviations[i] = deFloatAbs((float)v[i] - median);
63
64	return floatMedian(absoluteDeviations) / median;
65}
66
67static inline float dummyComputation (float initial, int numIterations)
68{
69	float	a = initial;
70	int		b = 123;
71
72	for (int i = 0; i < numIterations; i++)
73	{
74		// Arbitrary computations.
75		for (int j = 0; j < 4; j++)
76		{
77			a = deFloatCos(a + (float)b);
78			b = (b + 63) % 107 + de::abs((int)(a*10.0f));
79		}
80	}
81
82	return a + (float)b;
83}
84
85void warmupCPU (void)
86{
87	float	dummy				= *warmupCPUInternal::g_dummy.m_v;
88	int		computationSize		= 1;
89
90	// Do a rough calibration for computationSize to get dummyComputation's running time above a certain threshold.
91	while (computationSize < 1<<30) // \note This condition is unlikely to be met. The "real" loop exit is the break below.
92	{
93		const float		singleMeasurementThreshold	= 10000.0f;
94		const int		numMeasurements				= 3;
95		deInt64			times[numMeasurements];
96
97		for (int i = 0; i < numMeasurements; i++)
98		{
99			const deUint64 startTime = deGetMicroseconds();
100			dummy = dummyComputation(dummy, computationSize);
101			times[i] = (deInt64)(deGetMicroseconds() - startTime);
102		}
103
104		if (floatMedian(times) >= singleMeasurementThreshold)
105			break;
106
107		computationSize *= 2;
108	}
109
110	// Do dummyComputations until running time seems stable enough.
111	{
112		const int			maxNumMeasurements							= 50;
113		const int			numConsecutiveMeasurementsRequired			= 5;
114		const float			relativeMedianAbsoluteDeviationThreshold	= 0.05f;
115		deInt64				latestTimes[numConsecutiveMeasurementsRequired];
116
117		for (int measurementNdx = 0;
118
119			 measurementNdx < maxNumMeasurements &&
120			 (measurementNdx < numConsecutiveMeasurementsRequired ||
121			  floatRelativeMedianAbsoluteDeviation(latestTimes) > relativeMedianAbsoluteDeviationThreshold);
122
123			 measurementNdx++)
124		{
125			const deUint64 startTime = deGetMicroseconds();
126			dummy = dummyComputation(dummy, computationSize);
127			latestTimes[measurementNdx % numConsecutiveMeasurementsRequired] = (deInt64)(deGetMicroseconds() - startTime);
128		}
129	}
130
131	*warmupCPUInternal::g_dummy.m_v = dummy;
132}
133
134} // tcu
135