swift
samplesperformance.cpp
Go to the documentation of this file.
1 // SPDX-FileCopyrightText: Copyright (C) 2013 swift Project Community / Contributors
2 // SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-swift-pilot-client-1
3 
6 
7 #include "samplesperformance.h"
8 
9 #include <algorithm>
10 #include <iterator>
11 
12 #include <QDateTime>
13 #include <QElapsedTimer>
14 #include <QHash>
15 #include <QList>
16 #include <QRegExp>
17 #include <QRegularExpression>
18 #include <QRegularExpressionMatch>
19 #include <QString>
20 #include <QStringBuilder>
21 #include <QStringList>
22 #include <QTextStream>
23 #include <QVector>
24 #include <Qt>
25 
26 #include "core/db/databasereader.h"
30 #include "misc/aviation/altitude.h"
33 #include "misc/aviation/callsign.h"
35 #include "misc/directoryutils.h"
37 #include "misc/math/mathutils.h"
38 #include "misc/pq/units.h"
41 #include "misc/stringutils.h"
42 #include "misc/swiftdirectories.h"
43 #include "misc/test/testing.h"
44 
45 using namespace swift::misc;
46 using namespace swift::misc::aviation;
47 using namespace swift::misc::geo;
48 using namespace swift::misc::math;
49 using namespace swift::misc::network;
50 using namespace swift::misc::physical_quantities;
51 using namespace swift::misc::simulation;
52 using namespace swift::misc::test;
53 using namespace swift::core::db;
54 
55 namespace swift::sample
56 {
57  int CSamplesPerformance::samplesMisc(QTextStream &out)
58  {
59  QElapsedTimer timer;
60  int ms, number;
61  CSamplesPerformance::copy10kStations(1); // init
62 
63  // ATC stations, tradionally created
64  timer.start();
65  CAtcStationList atcs1 = CTesting::createAtcStations(10000);
66  ms = timer.elapsed();
67  out << "created (copy) " << atcs1.size() << " ATC stations in " << ms << "ms" << Qt::endl;
68 
69  timer.start();
70  CAtcStationList atcs2 = CTesting::createAtcStations(100000);
71  ms = timer.elapsed();
72  out << "created (copy) " << atcs2.size() << " ATC stations in " << ms << "ms" << Qt::endl;
73 
74  // ATC stations, property index created
75  timer.start();
76  CAtcStationList atcs3 = CTesting::createAtcStations(10000, true);
77  ms = timer.elapsed();
78  out << "created (propertyIndex) " << atcs3.size() << " ATC stations in " << ms << "ms" << Qt::endl;
79 
80  timer.start();
81  CAtcStationList atcs4 = CTesting::createAtcStations(100000, true);
82  ms = timer.elapsed();
83  out << "created (propertyIndex) " << atcs4.size() << " ATC stations in " << ms << "ms" << Qt::endl;
84 
85  // Sort by
86  timer.start();
88  ms = timer.elapsed();
89  out << "Sorted by callsign " << atcs1.size() << " ATC stations in " << ms << "ms" << Qt::endl;
90 
91  timer.start();
93  ms = timer.elapsed();
94  out << "Sorted by callsign " << atcs2.size() << " ATC stations in " << ms << "ms" << Qt::endl;
95 
96  // Read data, this is what all our models do when displaying in a table view
97  timer.start();
98  CSamplesPerformance::accessStationsData(atcs1, false);
99  ms = timer.elapsed();
100  out << "Read (getters) " << atcs1.size() << " ATC stations in " << ms << "ms" << Qt::endl;
101 
102  timer.start();
103  CSamplesPerformance::accessStationsData(atcs2, false);
104  ms = timer.elapsed();
105  out << "Read (getters) " << atcs2.size() << " ATC stations in " << ms << "ms" << Qt::endl;
106 
107  timer.start();
108  CSamplesPerformance::accessStationsData(atcs1, true);
109  ms = timer.elapsed();
110  out << "Read (propertyIndex) " << atcs1.size() << " ATC stations in " << ms << "ms" << Qt::endl;
111 
112  timer.start();
113  CSamplesPerformance::accessStationsData(atcs2, true);
114  ms = timer.elapsed();
115  out << "Read (propertyIndex) " << atcs2.size() << " ATC stations in " << ms << "ms" << Qt::endl;
116 
117  // calculate
118  number = 10000;
119  timer.start();
120  CSamplesPerformance::calculateDistance(number);
121  ms = timer.elapsed();
122  out << "Calculated distances " << number << " in " << ms << "ms" << Qt::endl;
123 
124  number = 100000;
125  timer.start();
126  CSamplesPerformance::calculateDistance(number);
127  ms = timer.elapsed();
128  out << "Calculated distances " << number << "in " << ms << "ms" << Qt::endl;
129 
130  // parse
131  number = 100000;
132  timer.start();
133  CSamplesPerformance::parseWgs(number);
134  ms = timer.elapsed();
135  out << "Parse WGS coordinates " << number << " in " << ms << "ms" << Qt::endl;
136 
137  // copy
138  timer.start();
139  number = 20;
140  CSamplesPerformance::copy10kStations(number);
141  ms = timer.elapsed();
142  out << "Copied 10k stations " << number << " times in " << ms << "ms" << Qt::endl;
143 
144  timer.start();
145  number = 100;
146  CSamplesPerformance::copy10kStations(number);
147  ms = timer.elapsed();
148  out << "Copied 10k stations " << number << " times in " << ms << "ms" << Qt::endl;
149 
150  // Regex pattern matching with lists of 10000 strings containing random hex numbers
151  auto generator = []() { return QString::number(CMathUtils::randomGenerator().generate(), 16); };
152  QStringList strList1, strList2, strList3, strList4;
153  std::generate_n(std::back_inserter(strList1), 100000, generator);
154  std::generate_n(std::back_inserter(strList2), 100000, generator);
155  std::generate_n(std::back_inserter(strList3), 100000, generator);
156  std::generate_n(std::back_inserter(strList4), 100000, generator);
157  QRegularExpression newRegex("^.*aaa.*$", QRegularExpression::CaseInsensitiveOption);
158  QRegExp fullRegex(".*aaa.*", Qt::CaseInsensitive);
159  QRegExp wildcardRegex("*aaa*", Qt::CaseInsensitive, QRegExp::Wildcard);
160  QString containsStr("aaa");
161  number = 0;
162  timer.start();
163  for (const auto &str : std::as_const(strList1))
164  {
165  if (newRegex.match(str).hasMatch()) number++;
166  }
167  ms = timer.elapsed();
168  out << "new regex matched " << number << " of" << strList1.size() << " strings in " << ms << "ms" << Qt::endl;
169  number = 0;
170  timer.start();
171  for (const auto &str : std::as_const(strList2))
172  {
173  if (fullRegex.exactMatch(str)) number++;
174  }
175  ms = timer.elapsed();
176  out << "full regex matched " << number << " of" << strList2.size() << " strings in " << ms << "ms" << Qt::endl;
177  number = 0;
178  timer.start();
179  for (const auto &str : std::as_const(strList3))
180  {
181  if (wildcardRegex.exactMatch(str)) number++;
182  }
183  ms = timer.elapsed();
184  out << "wildcard matched " << number << " of " << strList3.size() << " strings in " << ms << "ms" << Qt::endl;
185  number = 0;
186  timer.start();
187  for (const auto &str : std::as_const(strList4))
188  {
189  if (str.contains(containsStr)) number++;
190  }
191  ms = timer.elapsed();
192  out << "contains matched " << number << " of " << strList4.size() << " strings in " << ms << "ms" << Qt::endl;
193 
194  out << "-----------------------------------------------" << Qt::endl;
195  return EXIT_SUCCESS;
196  }
197 
198  int CSamplesPerformance::interpolatorScenario(QTextStream &out, int numberOfCallsigns, int numberOfTimes)
199  {
200  const qint64 baseTimeEpoch = QDateTime::currentMSecsSinceEpoch();
201  CAircraftSituationList situations = createSituations(baseTimeEpoch, numberOfCallsigns, numberOfTimes);
202  CAircraftSituationList situationsBefore;
203  CAircraftSituationList situationsAfter;
204 
205  qint64 halfTime = baseTimeEpoch + DeltaTime * numberOfTimes / 2;
206 
207  QElapsedTimer timer;
208  timer.start();
209  for (int cs = 0; cs < numberOfCallsigns; cs++)
210  {
211  CCallsign callsign("CS" + QString::number(cs));
212  situationsBefore = situations.findBefore(halfTime).findByCallsign(callsign);
213  situationsAfter = situations.findAfter(halfTime - 1).findByCallsign(callsign);
214  }
215  out << "Reads by time, callsigns: " << timer.elapsed() << "ms" << Qt::endl;
216 
217  timer.start();
218  situationsBefore = situations.findBefore(halfTime);
219  situationsAfter = situations.findAfter(halfTime - 1);
220  for (int cs = 0; cs < numberOfCallsigns; cs++)
221  {
222  CCallsign callsign("CS" + QString::number(cs));
223  CAircraftSituationList csSituationsBefore = situationsBefore.findByCallsign(callsign);
224  CAircraftSituationList csSituationsAfter = situationsAfter.findByCallsign(callsign);
225  Q_UNUSED(csSituationsBefore);
226  Q_UNUSED(csSituationsAfter);
227  }
228  out << "Split by time upfront, then callsigns: " << timer.elapsed() << "ms" << Qt::endl;
229  int b = situationsBefore.size();
230  int a = situationsAfter.size();
231  Q_ASSERT(a + b == numberOfTimes * numberOfCallsigns);
232  Q_UNUSED(a);
233  Q_UNUSED(b);
234 
235  timer.start();
236  const QHash<CCallsign, CAircraftSituationList> csSituations = situations.splitPerCallsign();
237  out << "Split by " << csSituations.size() << " callsigns, " << timer.elapsed() << "ms" << Qt::endl;
238 
239  timer.start();
240  for (const CAircraftSituationList &csl : csSituations)
241  {
242  CAircraftSituationList csSituationsBefore = csl.findBefore(halfTime);
243  CAircraftSituationList csSituationsAfter = csl.findAfter(halfTime - 1);
244  a = csSituationsBefore.size();
245  b = csSituationsAfter.size();
246  Q_ASSERT(a + b == numberOfTimes);
247  Q_UNUSED(csSituationsBefore);
248  Q_UNUSED(csSituationsAfter);
249  }
250  out << "Split by callsign, by time: " << timer.elapsed() << "ms" << Qt::endl;
251 
252  out << Qt::endl;
253  return EXIT_SUCCESS;
254  }
255 
256  int CSamplesPerformance::samplesJson(QTextStream &out)
257  {
258  QElapsedTimer timer;
259  auto situations = createSituations(0, 10000, 10);
260  auto models = createModels(10000, 100);
261 
262  timer.start();
263  QJsonObject json = situations.toJson();
264  out << "Convert 100,000 aircraft situations to JSON: " << timer.elapsed() << "ms" << Qt::endl;
265 
266  timer.start();
267  situations.convertFromJson(json);
268  out << "Convert 100,000 aircraft situations from JSON: " << timer.elapsed() << "ms" << Qt::endl
269  << Qt::endl;
270 
271  timer.start();
272  json = models.toJson();
273  out << "Convert 10,000 aircraft models to JSON (naive): " << timer.elapsed() << "ms" << Qt::endl;
274 
275  timer.start();
276  models.convertFromJson(json);
277  out << "Convert 10,000 aircraft models from JSON (naive): " << timer.elapsed() << "ms" << Qt::endl
278  << Qt::endl;
279 
280  timer.start();
281  json = models.toMemoizedJson();
282  out << "Convert 10,000 aircraft models to JSON (memoize): " << timer.elapsed() << "ms" << Qt::endl;
283 
284  timer.start();
285  models.convertFromMemoizedJson(json);
286  out << "Convert 10,000 aircraft models from JSON (memoize): " << timer.elapsed() << "ms" << Qt::endl
287  << Qt::endl;
288 
289  return EXIT_SUCCESS;
290  }
291 
292  int CSamplesPerformance::samplesJsonModelAndLivery(QTextStream &out)
293  {
294  const QString dir = CSwiftDirectories::staticDbFilesDirectory();
295  const QString modelFileName = QDir(dir).filePath("models.json");
296  const QString liveriesFileName = QDir(dir).filePath("liveries.json");
297 
298  QFile modelFile(modelFileName);
299  Q_ASSERT_X(modelFile.exists(), Q_FUNC_INFO, "Model file does not exist");
300  QFile liveryFile(liveriesFileName);
301  Q_ASSERT_X(liveryFile.exists(), Q_FUNC_INFO, "Liveries file does not exist");
302 
303  out << "Loaded DB JSON model file " << modelFile.fileName() << Qt::endl;
304  const QString modelData = CFileUtils::readFileToString(modelFile.fileName());
305  Q_ASSERT_X(!modelData.isEmpty(), Q_FUNC_INFO, "Model file empty");
306 
307  out << "Loaded DB JSON livery file " << liveryFile.fileName() << Qt::endl;
308  const QString liveryData = CFileUtils::readFileToString(liveryFile.fileName());
309  Q_ASSERT_X(!liveryData.isEmpty(), Q_FUNC_INFO, "Livery file empty");
310 
311  // DB format, all models denormalized in DB JSON format
313  QElapsedTimer timer;
314 
315  CDatabaseReader::stringToDatastoreResponse(liveryData, response);
316  timer.start();
317  const CLiveryList dbLiveries = CLiveryList::fromDatabaseJson(response);
318  int ms = timer.elapsed();
319  out << "Read via DB JSON format: " << dbLiveries.size() << " liveries in " << ms << "ms" << Qt::endl;
320 
321  // does not result in better performance, liveries/airlines have almost a 1:1 ratio
322  // unlike models' fromDatabaseJsonCaching not many airlines will be recycled
323  timer.start();
324  const CLiveryList dbLiveries2 = CLiveryList::fromDatabaseJsonCaching(response);
325  ms = timer.elapsed();
326  out << "Read via DB JSON format (new): " << dbLiveries2.size() << " liveries in " << ms << "ms" << Qt::endl;
327 
328  const CAirlineIcaoCodeList liveryAirlines = dbLiveries2.getAirlines();
329  timer.start();
330  const CLiveryList dbLiveries3 = CLiveryList::fromDatabaseJsonCaching(response, liveryAirlines);
331  ms = timer.elapsed();
332  out << "Read via DB JSON format (new, passing airlines): " << dbLiveries3.size() << " liveries in " << ms
333  << "ms" << Qt::endl;
334 
335  CDatabaseReader::stringToDatastoreResponse(modelData, response);
336  timer.start();
337  const CAircraftModelList dbModels = CAircraftModelList::fromDatabaseJson(response);
338  ms = timer.elapsed();
339  out << "Read via DB JSON format: " << dbModels.size() << " models in " << ms << "ms" << Qt::endl;
340 
341  timer.start();
342  const CAircraftModelList dbModels2 = CAircraftModelList::fromDatabaseJsonCaching(response);
343  ms = timer.elapsed();
344  out << "Read via DB JSON format (new): " << dbModels2.size() << " models in " << ms << "ms" << Qt::endl;
345 
346  // swift JSON format
347  const QJsonObject swiftJsonObject = dbModels.toJson();
348  out << "Converted to swift JSON" << Qt::endl;
349 
350  CAircraftModelList swiftModels;
351  timer.start();
352  swiftModels.convertFromJson(swiftJsonObject);
353  ms = timer.elapsed();
354  out << "Read via swift JSON format: " << swiftModels.size() << " models in " << ms << "ms" << Qt::endl;
355 
356  Q_ASSERT_X(swiftModels.size() == dbModels.size(), Q_FUNC_INFO, "Mismatching container size");
357  return EXIT_SUCCESS;
358  }
359 
360  int CSamplesPerformance::samplesStringUtilsVsRegEx(QTextStream &out)
361  {
362  QElapsedTimer timer;
363  static const QString chars =
364  "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789~_-=+!\"@#$%^&*()[]{} \t;:\\/?,.<>";
365  QStringList strings;
366  std::generate_n(std::back_inserter(strings), 100000, [] {
367  QString s;
368  std::generate_n(std::back_inserter(s), 10,
369  [] { return chars[CMathUtils::randomInteger(0, chars.size() - 1)]; });
370  return s;
371  });
372  QString bigString = strings.join("\n");
373 
374  QRegularExpression upperRegex("[A-Z]");
375  upperRegex.optimize();
376 
377  timer.start();
378  for (const QString &s : std::as_const(strings))
379  {
380  auto c = containsChar(s, [](QChar c) { return c.isUpper(); });
381  Q_UNUSED(c);
382  }
383  out << "Check 100,000 strings for containing uppercase letter: (utility) " << timer.elapsed() << "ms"
384  << Qt::endl;
385 
386  timer.start();
387  for (const QString &s : std::as_const(strings))
388  {
389  auto c = s.contains(upperRegex);
390  Q_UNUSED(c);
391  }
392  out << "Check 100,000 strings for containing uppercase letter: (regex) " << timer.elapsed() << "ms"
393  << Qt::endl
394  << Qt::endl;
395 
396  timer.start();
397  for (const QString &s : std::as_const(strings))
398  {
399  auto i = indexOfChar(s, [](QChar c) { return c.isUpper(); });
400  Q_UNUSED(i);
401  }
402  out << "Check 100,000 strings for index of first uppercase letter: (utility) " << timer.elapsed() << "ms"
403  << Qt::endl;
404 
405  timer.start();
406  for (const QString &s : std::as_const(strings))
407  {
408  auto i = s.indexOf(upperRegex);
409  Q_UNUSED(i);
410  }
411  out << "Check 100,000 strings for index of first uppercase letter: (regex) " << timer.elapsed() << "ms"
412  << Qt::endl
413  << Qt::endl;
414 
415  auto temp = strings;
416  timer.start();
417  for (QString &s : strings)
418  {
419  removeChars(s, [](QChar c) { return c.isUpper(); });
420  }
421  out << "Remove from 100,000 strings all uppercase letters: (utility) " << timer.elapsed() << "ms"
422  << Qt::endl;
423  strings = temp;
424 
425  timer.start();
426  for (QString &s : strings) { s.remove(upperRegex); }
427  out << "Remove from 100,000 strings all uppercase letters: (regex) " << timer.elapsed() << "ms"
428  << Qt::endl
429  << Qt::endl;
430 
431  timer.start();
432  {
433  auto lines = splitLines(bigString);
434  Q_UNUSED(lines);
435  }
436  out << "Split 100,000 line string into list of lines: (QStringList) " << timer.elapsed() << "ms"
437  << Qt::endl;
438 
439  timer.start();
440  {
441  auto lines = splitLinesRefs(bigString);
442  Q_UNUSED(lines);
443  }
444  out << "Split 100,000 line string into list of lines: (QList<QStringRef>) " << timer.elapsed() << "ms"
445  << Qt::endl;
446 
447  return EXIT_SUCCESS;
448  }
449 
450  int CSamplesPerformance::samplesStringConcat(QTextStream &out)
451  {
452  const int loop = 250000;
453  QString x;
454  const QString x1 = "11-1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
455  const QString x2 = "22-1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
456  const QString x3 = "33-1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
457  const QString x4 = "44-1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
458  const QString x5 = "55-1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
459  const QString x6 = "66-1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
460  const QString x7 = "77-1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
461  const QString x8 = "88-1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
462  const QString x9 = "99-1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
463 
464  QElapsedTimer time;
465  time.start();
466  for (int i = 0; i < loop; i++) { x += "12-1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; }
467  out << "+= String " << time.elapsed() << "ms" << Qt::endl;
468  x.clear();
469 
470  time.start();
471  for (int i = 0; i < loop; i++)
472  {
473  x += QLatin1String("12-1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ");
474  }
475  out << "+= QLatin1String " << time.elapsed() << "ms" << Qt::endl;
476  x.clear();
477 
478  time.start();
479  for (int i = 0; i < loop; i++)
480  {
481  x += QStringLiteral("12-1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ");
482  }
483  out << "+= QStringLiteral " << time.elapsed() << "ms" << Qt::endl;
484  x.clear();
485 
486  time.start();
487  for (int i = 0; i < loop; i++) { x = x1 + x2 + x3 + x4 + x5 + x6 + x7 + x8 + x9; }
488  out << "+ String multiple " << time.elapsed() << "ms" << Qt::endl;
489  x.clear();
490 
491  time.start();
492  for (int i = 0; i < loop; i++) { x = x1 % x2 % x3 % x4 % x5 % x6 % x7 % x8 % x9; }
493  out << "% String multiple " << time.elapsed() << "ms" << Qt::endl;
494  x.clear();
495 
496  time.start();
497  for (int i = 0; i < loop; i++)
498  {
499  x = x.append(x1).append(x2).append(x3).append(x4).append(x5).append(x6).append(x7).append(x8).append(x9);
500  }
501  out << "append String multiple " << time.elapsed() << "ms" << Qt::endl;
502  x.clear();
503 
504  static const QString xArgString("%1 %2 %3 %4 %5 %6 %7 %8 %9");
505  time.start();
506  for (int i = 0; i < loop; i++) { x = xArgString.arg(x1, x2, x3, x4, x5, x6, x7, x8, x9); }
507  out << "arg String multiple " << time.elapsed() << "ms" << Qt::endl;
508  x.clear();
509 
510  time.start();
511  for (int i = 0; i < loop; i++)
512  {
513  x = QStringLiteral("%1 %2 %3 %4 %5 %6 %7 %8 %9").arg(x1, x2, x3, x4, x5, x6, x7, x8, x9);
514  }
515  out << "arg QStringLiteral multiple " << time.elapsed() << "ms" << Qt::endl;
516  x.clear();
517 
518  return EXIT_SUCCESS;
519  }
520 
521  int CSamplesPerformance::samplesStringLiteralVsConstQString(QTextStream &out)
522  {
523  const int loop = 1e7;
524  QElapsedTimer time;
525  QString x;
526  time.start();
527  for (int i = 0; i < loop; i++) { x = fooString(); }
528  out << "by constQString " << time.elapsed() << "ms" << Qt::endl;
529  x.clear();
530 
531  time.start();
532  for (int i = 0; i < loop; i++) { x = fooStringLiteral(); }
533  out << "by QStringLiteral " << time.elapsed() << "ms" << Qt::endl;
534  x.clear();
535 
536  time.start();
537  for (int i = 0; i < loop; i++)
538  {
539  x = QString("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent libero. "
540  "Sed cursus ante dapibus diam. Sed nisi.");
541  }
542  out << "by QString(\"...\") " << time.elapsed() << "ms" << Qt::endl;
543  x.clear();
544 
545  time.start();
546  for (int i = 0; i < loop; i++)
547  {
548  x = QStringLiteral("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent "
549  "libero. Sed cursus ante dapibus diam. Sed nisi.");
550  }
551  out << "by QStringLiteral(\"...\") " << time.elapsed() << "ms" << Qt::endl;
552  x.clear();
553 
554  time.start();
555  for (int i = 0; i < loop; i++)
556  {
557  QStringList foo = generateList();
558  Q_UNUSED(foo.size());
559  }
560  out << "generated list " << time.elapsed() << "ms" << Qt::endl;
561 
562  time.start();
563  for (int i = 0; i < loop; i++)
564  {
565  QStringList foo = replacedList();
566  Q_UNUSED(foo.size());
567  }
568  out << "replaced list " << time.elapsed() << "ms" << Qt::endl;
569 
570  return EXIT_SUCCESS;
571  }
572 
573  int CSamplesPerformance::sampleQMapVsQHashByCallsign(QTextStream &out)
574  {
575  const CCallsignSet cs10 = CSamplesPerformance::callsigns(10);
576  const CCallsignSet cs25 = CSamplesPerformance::callsigns(25);
577  const CCallsignSet cs50 = CSamplesPerformance::callsigns(50);
578 
579  const QMap<CCallsign, CAircraftSituation> m10 = CSamplesPerformance::situationsMap(cs10);
580  const QMap<CCallsign, CAircraftSituation> m25 = CSamplesPerformance::situationsMap(cs25);
581  const QMap<CCallsign, CAircraftSituation> m50 = CSamplesPerformance::situationsMap(cs50);
582 
583  const QHash<CCallsign, CAircraftSituation> h10 = CSamplesPerformance::situationsHash(cs10);
584  const QHash<CCallsign, CAircraftSituation> h25 = CSamplesPerformance::situationsHash(cs25);
585  const QHash<CCallsign, CAircraftSituation> h50 = CSamplesPerformance::situationsHash(cs50);
586 
587  Q_ASSERT(m10.size() == 10 && h10.size() == 10);
588  Q_ASSERT(m25.size() == 25 && h25.size() == 25);
589  Q_ASSERT(m50.size() == 50 && h50.size() == 50);
590 
591  // QList since we have to add callsigns multiple times, set does not allow that
592  QList<CCallsign> cs_10_100_rnd;
593  QList<CCallsign> cs_25_100_rnd;
594  QList<CCallsign> cs_50_100_rnd;
595 
596  for (int i = 0; i < 20; ++i)
597  {
598  cs_10_100_rnd.append(cs10.randomElements(5).toQList());
599  cs_25_100_rnd.append(cs25.randomElements(5).toQList());
600  cs_50_100_rnd.append(cs50.randomElements(5).toQList());
601  }
602 
603  Q_ASSERT(cs_10_100_rnd.size() == 100);
604  Q_ASSERT(cs_25_100_rnd.size() == 100);
605  Q_ASSERT(cs_50_100_rnd.size() == 100);
606 
607  QElapsedTimer time;
608  time.start();
609  for (int i = 1; i < 10000; ++i)
610  {
611  for (const CCallsign &cs : cs_10_100_rnd)
612  {
613  CAircraftSituation s = m10[cs];
614  Q_ASSERT_X(s.getCallsign() == cs, Q_FUNC_INFO, "Wromg callsign");
615  }
616  }
617  out << "map 100 out of 10: " << time.elapsed() << "ms" << Qt::endl;
618 
619  time.start();
620  for (int i = 1; i < 10000; ++i)
621  {
622  for (const CCallsign &cs : cs_10_100_rnd)
623  {
624  CAircraftSituation s = h10[cs];
625  Q_ASSERT_X(s.getCallsign() == cs, Q_FUNC_INFO, "Wromg callsign");
626  }
627  }
628  out << "hash 100 out of 10: " << time.elapsed() << "ms" << Qt::endl;
629 
630  time.start();
631  for (int i = 1; i < 10000; ++i)
632  {
633  for (const CCallsign &cs : cs_25_100_rnd)
634  {
635  CAircraftSituation s = m25[cs];
636  Q_ASSERT_X(s.getCallsign() == cs, Q_FUNC_INFO, "Wromg callsign");
637  }
638  }
639  out << "map 100 out of 25: " << time.elapsed() << "ms" << Qt::endl;
640 
641  time.start();
642  for (int i = 1; i < 10000; ++i)
643  {
644  for (const CCallsign &cs : cs_25_100_rnd)
645  {
646  CAircraftSituation s = h25[cs];
647  Q_ASSERT_X(s.getCallsign() == cs, Q_FUNC_INFO, "Wromg callsign");
648  }
649  }
650  out << "hash 100 out of 25: " << time.elapsed() << "ms" << Qt::endl;
651 
652  time.start();
653  for (int i = 1; i < 10000; ++i)
654  {
655  for (const CCallsign &cs : cs_50_100_rnd)
656  {
657  CAircraftSituation s = m50[cs];
658  Q_ASSERT_X(s.getCallsign() == cs, Q_FUNC_INFO, "Wromg callsign");
659  }
660  }
661  out << "map 100 out of 50: " << time.elapsed() << "ms" << Qt::endl;
662 
663  time.start();
664  for (int i = 1; i < 10000; ++i)
665  {
666  for (const CCallsign &cs : cs_50_100_rnd)
667  {
668  CAircraftSituation s = h50[cs];
669  Q_ASSERT_X(s.getCallsign() == cs, Q_FUNC_INFO, "Wromg callsign");
670  }
671  }
672  out << "hash 100 out of 50: " << time.elapsed() << "ms" << Qt::endl;
673 
674  return EXIT_SUCCESS;
675  }
676 
677  CAircraftSituationList CSamplesPerformance::createSituations(qint64 baseTimeEpoch, int numberOfCallsigns,
678  int numberOfTimes)
679  {
680  CAircraftSituationList situations;
681  for (int cs = 0; cs < numberOfCallsigns; cs++)
682  {
683  CCallsign callsign("CS" + QString::number(cs));
684  CCoordinateGeodetic coordinate(cs, cs, cs);
685  for (int t = 0; t < numberOfTimes; t++)
686  {
687  CAircraftSituation s(callsign, coordinate);
688  s.setMSecsSinceEpoch(baseTimeEpoch + DeltaTime * t);
689  situations.push_back(s);
690  }
691  }
692  return situations;
693  }
694 
695  CAircraftModelList CSamplesPerformance::createModels(int numberOfModels, int numberOfMemoParts)
696  {
697  CAircraftIcaoCodeList aircraftIcaos;
698  CLiveryList liveries;
699  CDistributorList distributors;
700  for (int i = 0; i < numberOfMemoParts; ++i)
701  {
702  aircraftIcaos.push_back(CAircraftIcaoCode("A" + QString::number(i), "A" + QString::number(i), "L1P", "Lego",
703  "Foo", CWakeTurbulenceCategory::MEDIUM, false, false, false, 0));
704  liveries.push_back(CLivery(
705  "A" + QString::number(i),
706  CAirlineIcaoCode("A" + QString::number(i), "Foo", CCountry("DE", "Germany"), "Foo", false, false),
707  "Foo", "red", "blue", false));
708  distributors.push_back(CDistributor(QString::number(i), "Foo", {}, {}, CSimulatorInfo::FSX));
709  }
710 
711  CAircraftModelList models;
712  for (int i = 0; i < numberOfModels; ++i)
713  {
714  const auto &aircraftIcao = aircraftIcaos[CMathUtils::randomInteger(0, numberOfMemoParts - 1)];
715  const auto &livery = liveries[CMathUtils::randomInteger(0, numberOfMemoParts - 1)];
716  const auto &distributor = distributors[CMathUtils::randomInteger(0, numberOfMemoParts - 1)];
717  models.push_back(CAircraftModel(QString::number(i), CAircraftModel::TypeUnknown, CSimulatorInfo::FSX,
718  QString::number(i), QString::number(i), aircraftIcao, livery));
719  models.back().setDistributor(distributor);
720  }
721  return models;
722  }
723 
724  void CSamplesPerformance::calculateDistance(int n)
725  {
726  if (n < 1) { return; }
727  CAtcStation atc = CTesting::createStation(1);
728  const QList<CCoordinateGeodetic> pos(
729  { CCoordinateGeodetic(10.0, 10.0, 10.0), CCoordinateGeodetic(20.0, 20.0, 20.0),
730  CCoordinateGeodetic(30.0, 30.0, 30.0), CCoordinateGeodetic(40.0, 40.0, 40.0),
731  CCoordinateGeodetic(50.0, 50.0, 50.0), CCoordinateGeodetic(60.0, 60.0, 60.0),
732  CCoordinateGeodetic(70.0, 70.0, 70.0) });
733  const int s = pos.size();
734  for (int i = 0; i < n; i++)
735  {
736  int p = i % s;
738  }
739  }
740 
741  void CSamplesPerformance::copy10kStations(int times)
742  {
743  CAtcStationList stations;
744  for (int i = 0; i < times; i++)
745  {
746  stations = stations10k();
747  stations.pop_back(); // make sure stations are really copied (copy-on-write)
748  }
749  }
750 
751  void CSamplesPerformance::parseWgs(int times)
752  {
753  static QStringList wgsLatLng({ "12° 11′ 10″ N", "11° 22′ 33″ W", "48° 21′ 13″ N", "11° 47′ 09″ E",
754  " 8° 21′ 13″ N", "11° 47′ 09″ W", "18° 21′ 13″ S", "11° 47′ 09″ E",
755  "09° 12′ 13″ S", "11° 47′ 09″ W" });
756 
758  const CAltitude a(333, CLengthUnit::m());
759  for (int i = 0; i < times; i++)
760  {
761  int idx = (i % 5) * 2;
762  c = CCoordinateGeodetic::fromWgs84(wgsLatLng.at(idx), wgsLatLng.at(idx + 1), a);
763  }
764  }
765 
766  const QString &CSamplesPerformance::fooString()
767  {
768  static const QString s("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent "
769  "libero. Sed cursus ante dapibus diam. Sed nisi.");
770  return s;
771  }
772 
773  QString CSamplesPerformance::fooStringLiteral()
774  {
775  return QStringLiteral("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent "
776  "libero. Sed cursus ante dapibus diam. Sed nisi.");
777  }
778 
779  QStringList CSamplesPerformance::generateList() { return QStringList({ "1", "2", "3", "4" }); }
780 
781  QStringList CSamplesPerformance::replacedList()
782  {
783  static const QStringList l({ "1", "2", "3", "4" });
784  QStringList lc(l);
785  lc[1] = QStringLiteral("6");
786  lc[3] = QStringLiteral("7");
787  return lc;
788  }
789 
790  CCallsignSet CSamplesPerformance::callsigns(int number)
791  {
792  CCallsignSet set;
793  static const QString cs("FOO%1");
794  for (int i = 0; i < number; i++) { set.insert(CCallsign(cs.arg(i))); }
795  return set;
796  }
797 
798  QMap<CCallsign, CAircraftSituation> CSamplesPerformance::situationsMap(const CCallsignSet &callsigns)
799  {
801  for (const CCallsign &cs : callsigns)
802  {
803  const CAircraftSituation s(cs);
804  situations.insert(cs, s);
805  }
806  return situations;
807  }
808 
809  QHash<CCallsign, CAircraftSituation> CSamplesPerformance::situationsHash(const CCallsignSet &callsigns)
810  {
812  for (const CCallsign &cs : callsigns)
813  {
814  const CAircraftSituation s(cs);
815  situations.insert(cs, s);
816  }
817  return situations;
818  }
819 
820  const CAtcStationList &CSamplesPerformance::stations10k()
821  {
822  static const CAtcStationList s = CTesting::createAtcStations(10000, false);
823  return s;
824  }
825 
826  void CSamplesPerformance::accessStationsData(const CAtcStationList &stations, bool byPropertyIndex)
827  {
828  for (const CAtcStation &station : stations)
829  {
830  const QString s = CSamplesPerformance::accessStationData(station, byPropertyIndex);
831  Q_UNUSED(s);
832  }
833  }
834 
835  QString CSamplesPerformance::accessStationData(const CAtcStation &station, bool byPropertyIndex)
836  {
837  QString r;
838  if (byPropertyIndex)
839  {
840  r.append(station.propertyByIndex(CPropertyIndex { CAtcStation::IndexCallsign, CCallsign::IndexString })
841  .toString());
842  r.append(station.propertyByIndex(CPropertyIndex { CAtcStation::IndexController, CUser::IndexRealName })
843  .toString());
844  r.append(station
845  .propertyByIndex(
846  CPropertyIndex { CAtcStation::IndexPosition, CCoordinateGeodetic::IndexLatitudeAsString })
847  .toString());
848  r.append(station
849  .propertyByIndex(
850  CPropertyIndex { CAtcStation::IndexPosition, CCoordinateGeodetic::IndexLongitudeAsString })
851  .toString());
852  r.append(station
853  .propertyByIndex(CPropertyIndex { CAtcStation::IndexRelativeDistance,
854  CLength::IndexValueRounded2DigitsWithUnit })
855  .toString());
856  r.append(station.propertyByIndex(CPropertyIndex { CAtcStation::IndexLogoffTime })
857  .toDateTime()
858  .toString("yyyy-MM-dd hh:mm"));
859  }
860  else
861  {
862  r.append(station.getCallsignAsString());
863  r.append(station.getController().getRealName());
864  r.append(station.getPosition().latitudeAsString());
865  r.append(station.getPosition().longitudeAsString());
866  r.append(station.getRelativeDistance().toQString(true));
867  r.append(station.getLogoffTimeUtc().toString("yyyy-MM-dd hh:mm"));
868  }
869  return r;
870  }
871 } // namespace swift::sample
iterator insert(const_iterator hint, const T &value)
For compatibility with std::inserter.
Definition: collection.h:199
QJsonObject toJson() const
Cast to JSON object.
void convertFromJson(const QJsonObject &json)
Assign from JSON object.
static QString readFileToString(const QString &fileNameAndPath)
Read file into string.
Definition: fileutils.cpp:68
Derived randomElements(int n) const
Copy n elements from the container at random.
Definition: range.h:148
size_type size() const
Returns number of elements in the sequence.
Definition: sequence.h:273
void sortBy(K1 key1, Keys... keys)
In-place sort by some particular key(s).
Definition: sequence.h:576
void pop_back()
Removes an element at the end of the sequence.
Definition: sequence.h:367
void push_back(const T &value)
Appends an element at the end of the sequence.
Definition: sequence.h:305
reference back()
Access the last element.
Definition: sequence.h:249
static const QString & staticDbFilesDirectory()
Where static DB files are located.
CONTAINER findAfter(const QDateTime &dateTime) const
List of objects after dateTime (newer)
CONTAINER findBefore(const QDateTime &dateTime) const
List of objects before dateTime (older)
Value object for ICAO classification.
Value object encapsulating a list of ICAO codes.
Value object encapsulating information of an aircraft's situation.
const CCallsign & getCallsign() const
Corresponding callsign.
Value object for ICAO classification.
Value object encapsulating a list of ICAO codes.
Altitude as used in aviation, can be AGL or MSL altitude.
Definition: altitude.h:52
Value object encapsulating information about an ATC station.
Definition: atcstation.h:38
const CCallsign & getCallsign() const
Get callsign.
Definition: atcstation.h:84
const geo::CCoordinateGeodetic & getPosition() const
Get the position of the center of the controller's area of visibility.
Definition: atcstation.h:141
QString getCallsignAsString() const
Get callsign as string.
Definition: atcstation.h:90
QVariant propertyByIndex(CPropertyIndexRef index) const
Property by index.
Definition: atcstation.cpp:179
const swift::misc::network::CUser & getController() const
Get controller.
Definition: atcstation.h:105
const QDateTime & getLogoffTimeUtc() const
Return the expected logoff time (UTC). This data comes from the controller through its ATIS line.
Definition: atcstation.h:169
Value object for a list of ATC stations.
Value object encapsulating information of a callsign.
Definition: callsign.h:30
Value object for a set of callsigns.
Definition: callsignset.h:26
Value object encapsulating information about an airpot.
Definition: livery.h:29
Value object for a list of airports.
Definition: liverylist.h:29
CAirlineIcaoCodeList getAirlines() const
All aircraft codes.
Definition: liverylist.cpp:137
static CLiveryList fromDatabaseJsonCaching(const QJsonArray &array, const CAirlineIcaoCodeList &relatedAirlines={})
Caching version from DB data.
Definition: liverylist.cpp:194
CONTAINER findByCallsign(const CCallsign &callsign) const
Find 0..n stations by callsign.
QHash< CCallsign, CONTAINER > splitPerCallsign() const
Split into 0..n containers as per callsign.
static CLiveryList fromDatabaseJson(const QJsonArray &array)
From DB JSON with default prefixes.
QString longitudeAsString() const
Longitude as string.
QString latitudeAsString() const
Latitude as string.
const physical_quantities::CLength & getRelativeDistance() const
Get the distance.
physical_quantities::CLength calculcateAndUpdateRelativeDistance(const geo::ICoordinateGeodetic &position)
Calculcate distance, set it, and return distance.
QString toQString(bool i18n=false) const
Cast as QString.
Definition: mixinstring.h:76
const QString & getRealName() const
Get full name.
Definition: user.h:59
Aircraft model (used by another pilot, my models on disk)
Definition: aircraftmodel.h:71
void setDistributor(const CDistributor &distributor)
Set distributor.
Value object encapsulating a list of aircraft models.
Value object encapsulating information of software distributor.
Definition: distributor.h:33
Value object encapsulating a list of distributors.
Classes interacting with the swift database (aka "datastore").
Generate data for testing purposes.
Definition: testdata.h:45
Free functions in swift::misc.
QString removeChars(const QString &s, F predicate)
Return a string with characters removed that match the given predicate.
Definition: stringutils.h:35
int indexOfChar(const QString &s, F predicate)
Index of first character in the string matching the given predicate, or -1 if not found.
Definition: stringutils.h:78
SWIFT_MISC_EXPORT QStringList splitLines(const QString &s)
Split a string into multiple lines. Blank lines are skipped.
bool containsChar(const QString &s, F predicate)
True if any character in the string matches the given predicate.
Definition: stringutils.h:65
SWIFT_MISC_EXPORT QList< QStringRef > splitLinesRefs(const QString &s)
Split a string into multiple lines. Blank lines are skipped.
Response from our database (depending on JSON DB backend generates)