1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include <QtTest/qbenchmark.h>
5#include <QtTest/private/qbenchmark_p.h>
6#include <QtTest/private/qbenchmarkmetric_p.h>
7#include <QtTest/private/qbenchmarktimemeasurers_p.h>
8
9#include <QtCore/qdir.h>
10#include <QtCore/qset.h>
11#include <QtCore/qdebug.h>
12
13QT_BEGIN_NAMESPACE
14
15QBenchmarkGlobalData *QBenchmarkGlobalData::current;
16
17QBenchmarkGlobalData::QBenchmarkGlobalData()
18{
19 setMode(mode_);
20}
21
22QBenchmarkGlobalData::~QBenchmarkGlobalData()
23{
24 delete measurer;
25 if (QBenchmarkGlobalData::current == this)
26 QBenchmarkGlobalData::current = nullptr;
27}
28
29void QBenchmarkGlobalData::setMode(Mode mode)
30{
31 mode_ = mode;
32
33 delete measurer;
34 measurer = createMeasurer();
35}
36
37QBenchmarkMeasurerBase * QBenchmarkGlobalData::createMeasurer()
38{
39 QBenchmarkMeasurerBase *measurer = nullptr;
40 if (0) {
41#if QT_CONFIG(valgrind)
42 } else if (mode_ == CallgrindChildProcess || mode_ == CallgrindParentProcess) {
43 measurer = new QBenchmarkCallgrindMeasurer;
44#endif
45#ifdef QTESTLIB_USE_PERF_EVENTS
46 } else if (mode_ == PerfCounter) {
47 measurer = new QBenchmarkPerfEventsMeasurer;
48#endif
49#ifdef HAVE_TICK_COUNTER
50 } else if (mode_ == TickCounter) {
51 measurer = new QBenchmarkTickMeasurer;
52#endif
53 } else if (mode_ == EventCounter) {
54 measurer = new QBenchmarkEvent;
55 } else {
56 measurer = new QBenchmarkTimeMeasurer;
57 }
58 measurer->init();
59 return measurer;
60}
61
62int QBenchmarkGlobalData::adjustMedianIterationCount()
63{
64 return medianIterationCount != -1
65 ? medianIterationCount : measurer->adjustMedianCount(suggestion: 1);
66}
67
68
69QBenchmarkTestMethodData *QBenchmarkTestMethodData::current;
70
71QBenchmarkTestMethodData::QBenchmarkTestMethodData() = default;
72
73QBenchmarkTestMethodData::~QBenchmarkTestMethodData()
74{
75 QBenchmarkTestMethodData::current = nullptr;
76}
77
78void QBenchmarkTestMethodData::beginDataRun()
79{
80 iterationCount = adjustIterationCount(suggestion: 1);
81}
82
83void QBenchmarkTestMethodData::endDataRun()
84{
85}
86
87int QBenchmarkTestMethodData::adjustIterationCount(int suggestion)
88{
89 // Let the -iterations option override the measurer.
90 if (QBenchmarkGlobalData::current->iterationCount != -1) {
91 iterationCount = QBenchmarkGlobalData::current->iterationCount;
92 } else {
93 iterationCount = QBenchmarkGlobalData::current->measurer->adjustIterationCount(suggestion);
94 }
95
96 return iterationCount;
97}
98
99void QBenchmarkTestMethodData::setResults(const QList<QBenchmarkMeasurerBase::Measurement> &list,
100 bool setByMacro)
101{
102 bool accepted = false;
103 QBenchmarkMeasurerBase::Measurement firstMeasurement = {};
104 if (!list.isEmpty())
105 firstMeasurement = list.constFirst();
106
107 // Always accept the result if the iteration count has been
108 // specified on the command line with -iterations.
109 if (QBenchmarkGlobalData::current->iterationCount != -1)
110 accepted = true;
111
112 else if (QBenchmarkTestMethodData::current->runOnce || !setByMacro) {
113 iterationCount = 1;
114 accepted = true;
115 }
116
117 // Test the result directly without calling the measurer if the minimum time
118 // has been specified on the command line with -minimumvalue.
119 else if (QBenchmarkGlobalData::current->walltimeMinimum != -1)
120 accepted = (firstMeasurement.value > QBenchmarkGlobalData::current->walltimeMinimum);
121 else
122 accepted = QBenchmarkGlobalData::current->measurer->isMeasurementAccepted(m: firstMeasurement);
123
124 // Accept the result or double the number of iterations.
125 if (accepted)
126 resultAccepted = true;
127 else
128 iterationCount *= 2;
129
130 valid = true;
131 results.reserve(asize: list.size());
132 for (auto m : list)
133 results.emplaceBack(args&: QBenchmarkGlobalData::current->context, args&: m, args&: iterationCount, args&: setByMacro);
134}
135
136/*!
137 \class QTest::QBenchmarkIterationController
138 \internal
139
140 The QBenchmarkIterationController class is used by the QBENCHMARK macro to
141 drive the benchmarking loop. It is responsible for starting and stopping
142 the timing measurements as well as calling the result reporting functions.
143*/
144
145/*! \internal
146*/
147QTest::QBenchmarkIterationController::QBenchmarkIterationController(RunMode runMode)
148{
149 i = 0;
150 if (runMode == RunOnce)
151 QBenchmarkTestMethodData::current->runOnce = true;
152 QTest::beginBenchmarkMeasurement();
153}
154
155QTest::QBenchmarkIterationController::QBenchmarkIterationController()
156{
157 i = 0;
158 QTest::beginBenchmarkMeasurement();
159}
160
161/*! \internal
162*/
163QTest::QBenchmarkIterationController::~QBenchmarkIterationController()
164{
165 QBenchmarkTestMethodData::current->setResults(list: QTest::endBenchmarkMeasurement());
166}
167
168/*! \internal
169*/
170bool QTest::QBenchmarkIterationController::isDone()
171{
172 if (QBenchmarkTestMethodData::current->runOnce)
173 return i > 0;
174 return i >= QTest::iterationCount();
175}
176
177/*! \internal
178*/
179void QTest::QBenchmarkIterationController::next()
180{
181 ++i;
182}
183
184/*! \internal
185*/
186int QTest::iterationCount()
187{
188 return QBenchmarkTestMethodData::current->iterationCount;
189}
190
191/*! \internal
192*/
193void QTest::setIterationCountHint(int count)
194{
195 QBenchmarkTestMethodData::current->adjustIterationCount(suggestion: count);
196}
197
198/*! \internal
199*/
200void QTest::setIterationCount(int count)
201{
202 QBenchmarkTestMethodData::current->iterationCount = count;
203 QBenchmarkTestMethodData::current->resultAccepted = true;
204}
205
206/*! \internal
207*/
208void QTest::beginBenchmarkMeasurement()
209{
210 QBenchmarkGlobalData::current->measurer->start();
211 // the clock is ticking after the line above, don't add code here.
212}
213
214/*! \internal
215*/
216QList<QBenchmarkMeasurerBase::Measurement> QTest::endBenchmarkMeasurement()
217{
218 // the clock is ticking before the line below, don't add code here.
219 return QBenchmarkGlobalData::current->measurer->stop();
220}
221
222/*!
223 Sets the benchmark result for this test function to \a result.
224
225 Use this function if you want to report benchmark results without
226 using the QBENCHMARK macro. Use \a metric to specify how Qt Test
227 should interpret the results.
228
229 The context for the result will be the test function name and any
230 data tag from the _data function. This function can only be called
231 once in each test function, subsequent calls will replace the
232 earlier reported results.
233
234 Note that the -iterations command line argument has no effect
235 on test functions without the QBENCHMARK macro.
236
237 \since 4.7
238*/
239void QTest::setBenchmarkResult(qreal result, QTest::QBenchmarkMetric metric)
240{
241 QBenchmarkTestMethodData::current->setResult(m: { .value: result, .metric: metric }, setByMacro: false);
242}
243
244template <typename T>
245typename T::value_type qAverage(const T &container)
246{
247 typename T::const_iterator it = container.constBegin();
248 typename T::const_iterator end = container.constEnd();
249 typename T::value_type acc = typename T::value_type();
250 int count = 0;
251 while (it != end) {
252 acc += *it;
253 ++it;
254 ++count;
255 }
256 return acc / count;
257}
258
259QT_END_NAMESPACE
260

source code of qtbase/src/testlib/qbenchmark.cpp