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
49QT_BEGIN_NAMESPACE
50
51QBenchmarkGlobalData *QBenchmarkGlobalData::current;
52
53QBenchmarkGlobalData::QBenchmarkGlobalData()
54{
55 setMode(mode_);
56}
57
58QBenchmarkGlobalData::~QBenchmarkGlobalData()
59{
60 delete measurer;
61 if (QBenchmarkGlobalData::current == this)
62 QBenchmarkGlobalData::current = nullptr;
63}
64
65void QBenchmarkGlobalData::setMode(Mode mode)
66{
67 mode_ = mode;
68
69 delete measurer;
70 measurer = createMeasurer();
71}
72
73QBenchmarkMeasurerBase * 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
98int QBenchmarkGlobalData::adjustMedianIterationCount()
99{
100 return medianIterationCount != -1
101 ? medianIterationCount : measurer->adjustMedianCount(1);
102}
103
104
105QBenchmarkTestMethodData *QBenchmarkTestMethodData::current;
106
107QBenchmarkTestMethodData::QBenchmarkTestMethodData() = default;
108
109QBenchmarkTestMethodData::~QBenchmarkTestMethodData()
110{
111 QBenchmarkTestMethodData::current = nullptr;
112}
113
114void QBenchmarkTestMethodData::beginDataRun()
115{
116 iterationCount = adjustIterationCount(1);
117}
118
119void QBenchmarkTestMethodData::endDataRun()
120{
121}
122
123int 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
135void 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*/
178QTest::QBenchmarkIterationController::QBenchmarkIterationController(RunMode runMode)
179{
180 i = 0;
181 if (runMode == RunOnce)
182 QBenchmarkTestMethodData::current->runOnce = true;
183 QTest::beginBenchmarkMeasurement();
184}
185
186QTest::QBenchmarkIterationController::QBenchmarkIterationController()
187{
188 i = 0;
189 QTest::beginBenchmarkMeasurement();
190}
191
192/*! \internal
193*/
194QTest::QBenchmarkIterationController::~QBenchmarkIterationController()
195{
196 const qreal result = QTest::endBenchmarkMeasurement();
197 QBenchmarkTestMethodData::current->setResult(result, QBenchmarkGlobalData::current->measurer->metricType());
198}
199
200/*! \internal
201*/
202bool QTest::QBenchmarkIterationController::isDone()
203{
204 if (QBenchmarkTestMethodData::current->runOnce)
205 return i > 0;
206 return i >= QTest::iterationCount();
207}
208
209/*! \internal
210*/
211void QTest::QBenchmarkIterationController::next()
212{
213 ++i;
214}
215
216/*! \internal
217*/
218int QTest::iterationCount()
219{
220 return QBenchmarkTestMethodData::current->iterationCount;
221}
222
223/*! \internal
224*/
225void QTest::setIterationCountHint(int count)
226{
227 QBenchmarkTestMethodData::current->adjustIterationCount(count);
228}
229
230/*! \internal
231*/
232void QTest::setIterationCount(int count)
233{
234 QBenchmarkTestMethodData::current->iterationCount = count;
235 QBenchmarkTestMethodData::current->resultAccepted = true;
236}
237
238/*! \internal
239*/
240void 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*/
248quint64 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*/
271void QTest::setBenchmarkResult(qreal result, QTest::QBenchmarkMetric metric)
272{
273 QBenchmarkTestMethodData::current->setResult(result, metric, false);
274}
275
276template <typename T>
277typename 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
291QT_END_NAMESPACE
292