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 = initialIterationCount(1);
80}
81
82void QBenchmarkTestMethodData::endDataRun()
83{
84}
85
86/*!
87 \internal
88 Initial iteration count based on \a suggestion.
89
90 In qtestcase.cpp, TestMethods::invokeTestOnData() invokes the test method in
91 a loop until (the test fails or skips, or) the test run's duration exceeds a
92 certain minimum; setResults() doubles \c iterationCount when it doesn't
93 accept the result. In each call to the test method, the QBENCHMARK duly
94 performs \c iteractionCount cycles of its block of code, from which other
95 things make the above decisions about whether to double and try again.
96*/
97int QBenchmarkTestMethodData::initialIterationCount(int suggestion) const
98{
99 // Let the -iterations option override the measurer.
100 if (QBenchmarkGlobalData::current->iterationCount != -1)
101 return QBenchmarkGlobalData::current->iterationCount;
102
103 return QBenchmarkGlobalData::current->measurer->adjustIterationCount(suggestion);
104}
105
106void QBenchmarkTestMethodData::setResults(const QList<QBenchmarkMeasurerBase::Measurement> &list,
107 bool setByMacro)
108{
109 bool accepted = false;
110 QBenchmarkMeasurerBase::Measurement firstMeasurement = {};
111 if (!list.isEmpty())
112 firstMeasurement = list.constFirst();
113
114 // Always accept the result if the iteration count has been
115 // specified on the command line with -iterations.
116 if (QBenchmarkGlobalData::current->iterationCount != -1)
117 accepted = true;
118
119 else if (QBenchmarkTestMethodData::current->runOnce || !setByMacro) {
120 iterationCount = 1;
121 accepted = true;
122 }
123
124 // Test the result directly without calling the measurer if the minimum time
125 // has been specified on the command line with -minimumvalue.
126 else if (QBenchmarkGlobalData::current->walltimeMinimum != -1)
127 accepted = (firstMeasurement.value > QBenchmarkGlobalData::current->walltimeMinimum);
128 else
129 accepted = QBenchmarkGlobalData::current->measurer->isMeasurementAccepted(firstMeasurement);
130
131 QBenchmarkGlobalData::current->measurer->resetMetricsResults();
132 // Accept the result or double the number of iterations.
133 if (accepted)
134 resultAccepted = true;
135 else
136 iterationCount *= 2;
137
138 valid = true;
139 results.reserve(list.size());
140 for (auto m : list)
141 results.emplaceBack(QBenchmarkGlobalData::current->context, m, iterationCount, setByMacro);
142}
143
144/*!
145 \class QTest::QBenchmarkIterationController
146 \internal
147
148 The QBenchmarkIterationController class is used by the QBENCHMARK macro to
149 drive the benchmarking loop. It is responsible for starting and stopping
150 the timing measurements as well as calling the result reporting functions.
151*/
152
153/*! \internal
154*/
155QTest::QBenchmarkIterationController::QBenchmarkIterationController(RunMode runMode)
156{
157 i = 0;
158 QBenchmarkTestMethodData::current->setBlockSize();
159 if (runMode == RunOnce)
160 QBenchmarkTestMethodData::current->runOnce = true;
161 QTest::beginBenchmarkMeasurement();
162}
163
164QTest::QBenchmarkIterationController::QBenchmarkIterationController()
165{
166 i = 0;
167 QBenchmarkTestMethodData::current->setBlockSize();
168 QTest::beginBenchmarkMeasurement();
169}
170
171/*!
172 \internal
173 Split up iterations into a statistically useful number of blocks.
174
175 Record data in blocks. \c split is the desired number of blocks which is a
176 power of 2 like \c iterationcount. As long as split is below 32 we treat
177 each iteration as a block. When split is greater than 32 \c split becomes
178 a divisor for \c iterationcount. The goal is to reduce overhead from
179 measurements while calculating variance. The default \c split 32 should be
180 sufficient to get stable block-variance, from which the per-iteration
181 variance (estimator) can be computed by dividing by the block size.
182*/
183void QBenchmarkTestMethodData::setBlockSize(int split)
184{
185 measurementBlockSize = iterationCount > split ? iterationCount / split : 1;
186}
187
188/*! \internal
189*/
190QTest::QBenchmarkIterationController::~QBenchmarkIterationController()
191{
192 QBenchmarkTestMethodData::current->setResults(QTest::endBenchmarkMeasurement());
193}
194
195/*! \internal
196*/
197bool QTest::QBenchmarkIterationController::isDone() const noexcept
198{
199 if (QBenchmarkTestMethodData::current->runOnce)
200 return i > 0;
201 return i >= QTest::iterationCount();
202}
203
204/*! \internal
205*/
206void QTest::QBenchmarkIterationController::next() noexcept
207{
208 ++i;
209 // Record data once per block
210 if (i % QBenchmarkTestMethodData::current->measurementBlockSize == 0)
211 QBenchmarkGlobalData::current->measurer->updateMeasurement();
212}
213
214/*! \internal
215*/
216int QTest::iterationCount() noexcept
217{
218 return QBenchmarkTestMethodData::current->iterationCount;
219}
220
221/*! \internal
222*/
223void QTest::beginBenchmarkMeasurement()
224{
225 QBenchmarkGlobalData::current->measurer->start();
226 // the clock is ticking after the line above, don't add code here.
227}
228
229/*! \internal
230*/
231QList<QBenchmarkMeasurerBase::Measurement> QTest::endBenchmarkMeasurement()
232{
233 // the clock is ticking before the line below, don't add code here.
234 return QBenchmarkGlobalData::current->measurer->stop();
235}
236
237/*!
238 Sets the benchmark result for this test function to \a result.
239
240 Use this function if you want to report benchmark results without
241 using the QBENCHMARK macro. Use \a metric to specify how Qt Test
242 should interpret the results.
243
244 The context for the result will be the test function name and any
245 data tag from the _data function. This function can only be called
246 once in each test function, subsequent calls will replace the
247 earlier reported results.
248
249 Note that the -iterations command line argument has no effect
250 on test functions without the QBENCHMARK macro.
251
252 \since 4.7
253*/
254void QTest::setBenchmarkResult(qreal result, QTest::QBenchmarkMetric metric)
255{
256 QBenchmarkTestMethodData::current->setResult({ result, 0, metric }, false);
257}
258
259QT_END_NAMESPACE