Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qbenchmark.cpp
Go to the documentation of this file.
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
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 return measurer;
59}
60
61int QBenchmarkGlobalData::adjustMedianIterationCount()
62{
63 return medianIterationCount != -1
64 ? medianIterationCount : measurer->adjustMedianCount(1);
65}
66
67
68QBenchmarkTestMethodData *QBenchmarkTestMethodData::current;
69
70QBenchmarkTestMethodData::QBenchmarkTestMethodData() = default;
71
72QBenchmarkTestMethodData::~QBenchmarkTestMethodData()
73{
74 QBenchmarkTestMethodData::current = nullptr;
75}
76
77void QBenchmarkTestMethodData::beginDataRun()
78{
79 iterationCount = adjustIterationCount(1);
80}
81
82void QBenchmarkTestMethodData::endDataRun()
83{
84}
85
86int QBenchmarkTestMethodData::adjustIterationCount(int suggestion)
87{
88 // Let the -iterations option override the measurer.
89 if (QBenchmarkGlobalData::current->iterationCount != -1) {
90 iterationCount = QBenchmarkGlobalData::current->iterationCount;
91 } else {
92 iterationCount = QBenchmarkGlobalData::current->measurer->adjustIterationCount(suggestion);
93 }
94
95 return iterationCount;
96}
97
98void QBenchmarkTestMethodData::setResults(const QList<QBenchmarkMeasurerBase::Measurement> &list,
99 bool setByMacro)
100{
101 bool accepted = false;
102 QBenchmarkMeasurerBase::Measurement firstMeasurement = {};
103 if (!list.isEmpty())
104 firstMeasurement = list.constFirst();
105
106 // Always accept the result if the iteration count has been
107 // specified on the command line with -iterations.
108 if (QBenchmarkGlobalData::current->iterationCount != -1)
109 accepted = true;
110
111 else if (QBenchmarkTestMethodData::current->runOnce || !setByMacro) {
112 iterationCount = 1;
113 accepted = true;
114 }
115
116 // Test the result directly without calling the measurer if the minimum time
117 // has been specified on the command line with -minimumvalue.
118 else if (QBenchmarkGlobalData::current->walltimeMinimum != -1)
119 accepted = (firstMeasurement.value > QBenchmarkGlobalData::current->walltimeMinimum);
120 else
121 accepted = QBenchmarkGlobalData::current->measurer->isMeasurementAccepted(firstMeasurement);
122
123 QBenchmarkGlobalData::current->measurer->resetMetricsResults();
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(list.size());
132 for (auto m : list)
133 results.emplaceBack(QBenchmarkGlobalData::current->context, m, iterationCount, 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 QBenchmarkTestMethodData::current->setBlockSize();
151 if (runMode == RunOnce)
152 QBenchmarkTestMethodData::current->runOnce = true;
153 QTest::beginBenchmarkMeasurement();
154}
155
156QTest::QBenchmarkIterationController::QBenchmarkIterationController()
157{
158 i = 0;
159 QBenchmarkTestMethodData::current->setBlockSize();
160 QTest::beginBenchmarkMeasurement();
161}
162
163/*!
164 \internal
165 Split up iterations into a statistically useful number of blocks.
166
167 Record data in blocks. \c split is the desired number of blocks which is a
168 power of 2 like \c iterationcount. As long as split is below 32 we treat
169 each iteration as a block. When split is greater than 32 \c split becomes
170 a divisor for \c iterationcount. The goal is to reduce overhead from
171 measurements while calculating variance. The default \c split 32 should be
172 sufficient to get stable block-variance, from which the per-iteration
173 variance (estimator) can be computed by dividing by the block size.
174*/
175void QBenchmarkTestMethodData::setBlockSize(int split)
176{
177 measurementBlockSize = iterationCount > split ? iterationCount / split : 1;
178}
179
180/*! \internal
181*/
182QTest::QBenchmarkIterationController::~QBenchmarkIterationController()
183{
184 QBenchmarkTestMethodData::current->setResults(QTest::endBenchmarkMeasurement());
185}
186
187/*! \internal
188*/
189bool QTest::QBenchmarkIterationController::isDone() const noexcept
190{
191 if (QBenchmarkTestMethodData::current->runOnce)
192 return i > 0;
193 return i >= QTest::iterationCount();
194}
195
196/*! \internal
197*/
198void QTest::QBenchmarkIterationController::next() noexcept
199{
200 ++i;
201 // Record data once per block
202 if (i % QBenchmarkTestMethodData::current->measurementBlockSize == 0)
203 QBenchmarkGlobalData::current->measurer->updateMeasurement();
204}
205
206/*! \internal
207*/
208int QTest::iterationCount() noexcept
209{
210 return QBenchmarkTestMethodData::current->iterationCount;
211}
212
213/*! \internal
214*/
215void QTest::setIterationCountHint(int count)
216{
217 QBenchmarkTestMethodData::current->adjustIterationCount(count);
218}
219
220/*! \internal
221*/
222void QTest::setIterationCount(int count)
223{
224 QBenchmarkTestMethodData::current->iterationCount = count;
225 QBenchmarkTestMethodData::current->resultAccepted = true;
226}
227
228/*! \internal
229*/
230void QTest::beginBenchmarkMeasurement()
231{
232 QBenchmarkGlobalData::current->measurer->start();
233 // the clock is ticking after the line above, don't add code here.
234}
235
236/*! \internal
237*/
238QList<QBenchmarkMeasurerBase::Measurement> QTest::endBenchmarkMeasurement()
239{
240 // the clock is ticking before the line below, don't add code here.
241 return QBenchmarkGlobalData::current->measurer->stop();
242}
243
244/*!
245 Sets the benchmark result for this test function to \a result.
246
247 Use this function if you want to report benchmark results without
248 using the QBENCHMARK macro. Use \a metric to specify how Qt Test
249 should interpret the results.
250
251 The context for the result will be the test function name and any
252 data tag from the _data function. This function can only be called
253 once in each test function, subsequent calls will replace the
254 earlier reported results.
255
256 Note that the -iterations command line argument has no effect
257 on test functions without the QBENCHMARK macro.
258
259 \since 4.7
260*/
261void QTest::setBenchmarkResult(qreal result, QTest::QBenchmarkMetric metric)
262{
263 QBenchmarkTestMethodData::current->setResult({ result, 0, metric }, false);
264}
265
266QT_END_NAMESPACE