1 /*
2 SPDX-FileCopyrightText: 2012 Milian Wolff <mail@milianw.de>
3
4 SPDX-License-Identifier: LGPL-2.0-or-later
5 */
6
7 #include "bench_hashes.h"
8
9 #include <serialization/indexedstring.h>
10
11 #include <tests/testcore.h>
12 #include <tests/autotestshell.h>
13 #include <QDateTime>
14 #include <QVector>
15 #include <QTest>
16
17 #include <unordered_map>
18
19 // similar to e.g. modificationrevision.cpp
20 struct DataT
21 {
22 QDateTime a;
23 QDateTime b;
24 };
25
26 using DataPair = QPair<KDevelop::IndexedString, DataT>;
27 using InputData = QVector<DataPair>;
28
29 struct IndexedStringHash
30 {
operator ()IndexedStringHash31 inline uint operator()(const KDevelop::IndexedString& str) const
32 {
33 return str.hash();
34 }
35 };
36
37 using StlHash = std::unordered_map<KDevelop::IndexedString, DataT, IndexedStringHash>;
38
insertData(StlHash & hash,const InputData & data)39 inline void insertData(StlHash& hash, const InputData& data)
40 {
41 for (const DataPair& pair : data) {
42 hash.insert(std::make_pair(pair.first, pair.second));
43 }
44 }
45
46 using QStringHash = QHash<KDevelop::IndexedString, DataT>;
insertData(QStringHash & hash,const InputData & data)47 inline void insertData(QStringHash& hash, const InputData& data)
48 {
49 for (const DataPair& pair : data) {
50 hash.insert(pair.first, pair.second);
51 }
52 }
53
54 QTEST_GUILESS_MAIN(BenchHashes)
55
56 using namespace KDevelop;
57
Q_DECLARE_METATYPE(InputData)58 Q_DECLARE_METATYPE(InputData)
59
60 void BenchHashes::initTestCase()
61 {
62 AutoTestShell::init();
63 TestCore::initialize(Core::NoUi);
64
65 qRegisterMetaType<InputData>();
66 }
67
cleanupTestCase()68 void BenchHashes::cleanupTestCase()
69 {
70 TestCore::shutdown();
71 }
72
feedData()73 void BenchHashes::feedData()
74 {
75 QTest::addColumn<bool>("useStl");
76 QTest::addColumn<InputData>("data");
77
78 InputData data;
79 const QVector<int> sizes{100, 1000, 10000, 100000};
80 for (int size : sizes) {
81 for (int i = data.size(); i < size; ++i) {
82 data << qMakePair(IndexedString(QString::number(i)), DataT());
83 }
84
85 QCOMPARE(data.size(), size);
86 QTest::newRow(qPrintable(QStringLiteral("unordered_map-%1").arg(size)))
87 << true << data;
88 QTest::newRow(qPrintable(QStringLiteral("qhash-%1").arg(size)))
89 << false << data;
90 }
91 }
92
insert()93 void BenchHashes::insert()
94 {
95 QFETCH(bool, useStl);
96 QFETCH(InputData, data);
97
98 if (useStl) {
99 QBENCHMARK {
100 StlHash hash;
101 insertData(hash, data);
102 }
103 } else {
104 QBENCHMARK {
105 QStringHash hash;
106 insertData(hash, data);
107 }
108 }
109 }
110
insert_data()111 void BenchHashes::insert_data()
112 {
113 feedData();
114 }
115
find()116 void BenchHashes::find()
117 {
118 QFETCH(bool, useStl);
119 QFETCH(const InputData, data);
120
121 if (useStl) {
122 StlHash hash;
123 insertData(hash, data);
124 QBENCHMARK {
125 for (const DataPair& pair : data) {
126 ( void ) hash.find(pair.first);
127 }
128 }
129 } else {
130 QStringHash hash;
131 insertData(hash, data);
132 QBENCHMARK {
133 for (const DataPair& pair : data) {
134 ( void ) hash.find(pair.first);
135 }
136 }
137 }
138 }
139
find_data()140 void BenchHashes::find_data()
141 {
142 feedData();
143 }
144
constFind()145 void BenchHashes::constFind()
146 {
147 QFETCH(bool, useStl);
148 QFETCH(const InputData, data);
149
150 if (useStl) {
151 StlHash hash;
152 insertData(hash, data);
153 const StlHash& constHash = hash;
154 QBENCHMARK {
155 for (const DataPair& pair : data) {
156 ( void ) constHash.find(pair.first);
157 }
158 }
159 } else {
160 QStringHash hash;
161 insertData(hash, data);
162 QBENCHMARK {
163 for (const DataPair& pair : data) {
164 ( void ) hash.constFind(pair.first);
165 }
166 }
167 }
168 }
169
constFind_data()170 void BenchHashes::constFind_data()
171 {
172 feedData();
173 }
174
remove()175 void BenchHashes::remove()
176 {
177 QFETCH(bool, useStl);
178 QFETCH(const InputData, data);
179
180 if (useStl) {
181 StlHash hash;
182 insertData(hash, data);
183 QBENCHMARK {
184 for (const DataPair& pair : data) {
185 hash.erase(pair.first);
186 }
187 }
188 } else {
189 QStringHash hash;
190 insertData(hash, data);
191 QBENCHMARK {
192 for (const DataPair& pair : data) {
193 hash.remove(pair.first);
194 }
195 }
196 }
197 }
198
remove_data()199 void BenchHashes::remove_data()
200 {
201 feedData();
202 }
203
204 struct TypeRepoTestData
205 {
206 size_t size = 0;
207 void* ptr = nullptr;
208 };
209
210 /**
211 * somewhat artificial benchmark to test speed impact if we'd ever change
212 * the underlying data type of the TypeSystem / TypeRegister.
213 */
typeRepo()214 void BenchHashes::typeRepo()
215 {
216 QFETCH(int, type);
217 if (type == 1 || type == 2) {
218 QVector<TypeRepoTestData*> v;
219 for (int i = 0; i < 100; ++i) {
220 v.append(new TypeRepoTestData);
221 }
222
223 if (type == 1) {
224 QBENCHMARK {
225 for (int i = 0; i < 100; ++i) {
226 v.at(i)->size++;
227 }
228 }
229 } else if (type == 2) {
230 TypeRepoTestData** a = v.data();
231 QBENCHMARK {
232 for (int i = 0; i < 100; ++i) {
233 a[i]->size++;
234 }
235 }
236 }
237 qDeleteAll(v);
238 } else if (type == 3) {
239 QHash<int, TypeRepoTestData*> v;
240 for (int i = 0; i < 100; ++i) {
241 v[i] = new TypeRepoTestData;
242 }
243
244 QBENCHMARK {
245 for (int i = 0; i < 100; ++i) {
246 v.value(i)->size++;
247 }
248 }
249 qDeleteAll(v);
250 } else if (type == 4) {
251 QMap<int, TypeRepoTestData*> v;
252 for (int i = 0; i < 100; ++i) {
253 v[i] = new TypeRepoTestData;
254 }
255
256 QBENCHMARK {
257 for (int i = 0; i < 100; ++i) {
258 v.value(i)->size++;
259 }
260 }
261 qDeleteAll(v);
262 } else if (type == 5) {
263 std::unordered_map<int, TypeRepoTestData*> v;
264 for (int i = 0; i < 100; ++i) {
265 v[i] = new TypeRepoTestData;
266 }
267
268 QBENCHMARK {
269 for (int i = 0; i < 100; ++i) {
270 v.at(i)->size++;
271 }
272 }
273 for (const auto& [key, value] : v) {
274 delete value;
275 }
276 } else if (type == 6) {
277 // for the idea, look at c++'s lexer.cpp
278 const int vectors = 5;
279 using Pair = QPair<int, TypeRepoTestData*>;
280 using InnerVector = QVarLengthArray<Pair, vectors>;
281 QVarLengthArray <InnerVector, 10> v;
282 v.resize(vectors);
283 for (int i = 0; i < 100; ++i) {
284 v[i % vectors] << qMakePair(i, new TypeRepoTestData);
285 }
286
287 QBENCHMARK {
288 for (int i = 0; i < 100; ++i) {
289 for (const Pair& p : qAsConst(v.at(i % vectors))) {
290 if (p.first == i) {
291 p.second->size++;
292 break;
293 }
294 }
295 }
296 }
297 for (const auto& inner : v) {
298 for (const auto& [key, value] : inner) {
299 delete value;
300 }
301 }
302 } else if (type == 0) {
303 QBENCHMARK {}
304 }
305 }
306
typeRepo_data()307 void BenchHashes::typeRepo_data()
308 {
309 QTest::addColumn<int>("type");
310
311 QTest::newRow("noop") << 0;
312 QTest::newRow("vector") << 1;
313 QTest::newRow("vector-raw") << 2;
314 QTest::newRow("qhash") << 3;
315 QTest::newRow("qmap") << 4;
316 QTest::newRow("unordered_map") << 5;
317 QTest::newRow("nested-vector") << 6;
318 }
319