1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the QtTest module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 #include <QtTest/qbenchmark.h>
41 #include <QtTest/private/qbenchmark_p.h>
42 #include <QtTest/private/qbenchmarkmetric_p.h>
43 #include <QtTest/private/qbenchmarktimemeasurers_p.h>
44 
45 #include <QtCore/qdir.h>
46 #include <QtCore/qset.h>
47 #include <QtCore/qdebug.h>
48 
49 QT_BEGIN_NAMESPACE
50 
51 QBenchmarkGlobalData *QBenchmarkGlobalData::current;
52 
QBenchmarkGlobalData()53 QBenchmarkGlobalData::QBenchmarkGlobalData()
54 {
55     setMode(mode_);
56 }
57 
~QBenchmarkGlobalData()58 QBenchmarkGlobalData::~QBenchmarkGlobalData()
59 {
60     delete measurer;
61     if (QBenchmarkGlobalData::current == this)
62         QBenchmarkGlobalData::current = nullptr;
63 }
64 
setMode(Mode mode)65 void QBenchmarkGlobalData::setMode(Mode mode)
66 {
67     mode_ = mode;
68 
69     delete measurer;
70     measurer = createMeasurer();
71 }
72 
createMeasurer()73 QBenchmarkMeasurerBase * QBenchmarkGlobalData::createMeasurer()
74 {
75     QBenchmarkMeasurerBase *measurer = nullptr;
76     if (0) {
77 #if QT_CONFIG(valgrind)
78     } else if (mode_ == CallgrindChildProcess || mode_ == CallgrindParentProcess) {
79         measurer = new QBenchmarkCallgrindMeasurer;
80 #endif
81 #ifdef QTESTLIB_USE_PERF_EVENTS
82     } else if (mode_ == PerfCounter) {
83         measurer = new QBenchmarkPerfEventsMeasurer;
84 #endif
85 #ifdef HAVE_TICK_COUNTER
86     } else if (mode_ == TickCounter) {
87         measurer = new QBenchmarkTickMeasurer;
88 #endif
89     } else if (mode_ == EventCounter) {
90         measurer = new QBenchmarkEvent;
91     } else {
92         measurer =  new QBenchmarkTimeMeasurer;
93     }
94     measurer->init();
95     return measurer;
96 }
97 
adjustMedianIterationCount()98 int QBenchmarkGlobalData::adjustMedianIterationCount()
99 {
100     return medianIterationCount != -1
101         ? medianIterationCount : measurer->adjustMedianCount(1);
102 }
103 
104 
105 QBenchmarkTestMethodData *QBenchmarkTestMethodData::current;
106 
107 QBenchmarkTestMethodData::QBenchmarkTestMethodData() = default;
108 
~QBenchmarkTestMethodData()109 QBenchmarkTestMethodData::~QBenchmarkTestMethodData()
110 {
111     QBenchmarkTestMethodData::current = nullptr;
112 }
113 
beginDataRun()114 void QBenchmarkTestMethodData::beginDataRun()
115 {
116     iterationCount = adjustIterationCount(1);
117 }
118 
endDataRun()119 void QBenchmarkTestMethodData::endDataRun()
120 {
121 }
122 
adjustIterationCount(int suggestion)123 int QBenchmarkTestMethodData::adjustIterationCount(int suggestion)
124 {
125     // Let the -iterations option override the measurer.
126     if (QBenchmarkGlobalData::current->iterationCount != -1) {
127         iterationCount = QBenchmarkGlobalData::current->iterationCount;
128     } else {
129         iterationCount = QBenchmarkGlobalData::current->measurer->adjustIterationCount(suggestion);
130     }
131 
132     return iterationCount;
133 }
134 
setResult(qreal value,QTest::QBenchmarkMetric metric,bool setByMacro)135 void QBenchmarkTestMethodData::setResult(
136     qreal value, QTest::QBenchmarkMetric metric, bool setByMacro)
137 {
138     bool accepted = false;
139 
140     // Always accept the result if the iteration count has been
141     // specified on the command line with -iterations.
142     if (QBenchmarkGlobalData::current->iterationCount != -1)
143         accepted = true;
144 
145     else if (QBenchmarkTestMethodData::current->runOnce || !setByMacro) {
146         iterationCount = 1;
147         accepted = true;
148     }
149 
150     // Test the result directly without calling the measurer if the minimum time
151     // has been specified on the command line with -minimumvalue.
152     else if (QBenchmarkGlobalData::current->walltimeMinimum != -1)
153         accepted = (value > QBenchmarkGlobalData::current->walltimeMinimum);
154     else
155         accepted = QBenchmarkGlobalData::current->measurer->isMeasurementAccepted(value);
156 
157     // Accept the result or double the number of iterations.
158     if (accepted)
159         resultAccepted = true;
160     else
161         iterationCount *= 2;
162 
163     this->result = QBenchmarkResult(
164         QBenchmarkGlobalData::current->context, value, iterationCount, metric, setByMacro);
165 }
166 
167 /*!
168     \class QTest::QBenchmarkIterationController
169     \internal
170 
171     The QBenchmarkIterationController class is used by the QBENCHMARK macro to
172     drive the benchmarking loop. It is repsonsible for starting and stopping
173     the timing measurements as well as calling the result reporting functions.
174 */
175 
176 /*! \internal
177 */
QBenchmarkIterationController(RunMode runMode)178 QTest::QBenchmarkIterationController::QBenchmarkIterationController(RunMode runMode)
179 {
180     i = 0;
181     if (runMode == RunOnce)
182         QBenchmarkTestMethodData::current->runOnce = true;
183     QTest::beginBenchmarkMeasurement();
184 }
185 
QBenchmarkIterationController()186 QTest::QBenchmarkIterationController::QBenchmarkIterationController()
187 {
188     i = 0;
189     QTest::beginBenchmarkMeasurement();
190 }
191 
192 /*! \internal
193 */
~QBenchmarkIterationController()194 QTest::QBenchmarkIterationController::~QBenchmarkIterationController()
195 {
196     const qreal result = QTest::endBenchmarkMeasurement();
197     QBenchmarkTestMethodData::current->setResult(result, QBenchmarkGlobalData::current->measurer->metricType());
198 }
199 
200 /*! \internal
201 */
isDone()202 bool QTest::QBenchmarkIterationController::isDone()
203 {
204     if (QBenchmarkTestMethodData::current->runOnce)
205         return i > 0;
206     return i >= QTest::iterationCount();
207 }
208 
209 /*! \internal
210 */
next()211 void QTest::QBenchmarkIterationController::next()
212 {
213     ++i;
214 }
215 
216 /*! \internal
217 */
iterationCount()218 int QTest::iterationCount()
219 {
220     return QBenchmarkTestMethodData::current->iterationCount;
221 }
222 
223 /*! \internal
224 */
setIterationCountHint(int count)225 void QTest::setIterationCountHint(int count)
226 {
227     QBenchmarkTestMethodData::current->adjustIterationCount(count);
228 }
229 
230 /*! \internal
231 */
setIterationCount(int count)232 void QTest::setIterationCount(int count)
233 {
234     QBenchmarkTestMethodData::current->iterationCount = count;
235     QBenchmarkTestMethodData::current->resultAccepted = true;
236 }
237 
238 /*! \internal
239 */
beginBenchmarkMeasurement()240 void QTest::beginBenchmarkMeasurement()
241 {
242     QBenchmarkGlobalData::current->measurer->start();
243     // the clock is ticking after the line above, don't add code here.
244 }
245 
246 /*! \internal
247 */
endBenchmarkMeasurement()248 quint64 QTest::endBenchmarkMeasurement()
249 {
250     // the clock is ticking before the line below, don't add code here.
251     return QBenchmarkGlobalData::current->measurer->stop();
252 }
253 
254 /*!
255     Sets the benchmark result for this test function to \a result.
256 
257     Use this function if you want to report benchmark results without
258     using the QBENCHMARK macro. Use \a metric to specify how Qt Test
259     should interpret the results.
260 
261     The context for the result will be the test function name and any
262     data tag from the _data function. This function can only be called
263     once in each test function, subsequent calls will replace the
264     earlier reported results.
265 
266     Note that the -iterations command line argument has no effect
267     on test functions without the QBENCHMARK macro.
268 
269     \since 4.7
270 */
setBenchmarkResult(qreal result,QTest::QBenchmarkMetric metric)271 void QTest::setBenchmarkResult(qreal result, QTest::QBenchmarkMetric metric)
272 {
273     QBenchmarkTestMethodData::current->setResult(result, metric, false);
274 }
275 
276 template <typename T>
qAverage(const T & container)277 typename T::value_type qAverage(const T &container)
278 {
279     typename T::const_iterator it = container.constBegin();
280     typename T::const_iterator end = container.constEnd();
281     typename T::value_type acc = typename T::value_type();
282     int count = 0;
283     while (it != end) {
284         acc += *it;
285         ++it;
286         ++count;
287     }
288     return acc / count;
289 }
290 
291 QT_END_NAMESPACE
292