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 <QRegularExpression>
17 #include <QRegularExpressionMatch>
18 #include <QString>
19 #include <QStringBuilder>
20 #include <QStringList>
21 #include <QTextStream>
22 #include <QVector>
23 #include <Qt>
24 
25 #include "core/db/databasereader.h"
29 #include "misc/aviation/altitude.h"
32 #include "misc/aviation/callsign.h"
34 #include "misc/directoryutils.h"
36 #include "misc/math/mathutils.h"
37 #include "misc/pq/units.h"
40 #include "misc/stringutils.h"
41 #include "misc/swiftdirectories.h"
42 #include "misc/test/testing.h"
43 
44 using namespace swift::misc;
45 using namespace swift::misc::aviation;
46 using namespace swift::misc::geo;
47 using namespace swift::misc::math;
48 using namespace swift::misc::network;
49 using namespace swift::misc::physical_quantities;
50 using namespace swift::misc::simulation;
51 using namespace swift::misc::test;
52 using namespace swift::core::db;
53 
54 namespace swift::sample
55 {
56  int CSamplesPerformance::samplesMisc(QTextStream &out)
57  {
58  QElapsedTimer timer;
59  qint64 ms {};
60  int 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;
153  QStringList strList2;
154  QStringList strList3;
155  QStringList strList4;
156  std::generate_n(std::back_inserter(strList1), 100000, generator);
157  std::generate_n(std::back_inserter(strList2), 100000, generator);
158  std::generate_n(std::back_inserter(strList3), 100000, generator);
159  std::generate_n(std::back_inserter(strList4), 100000, generator);
160  QRegularExpression newRegex("^.*aaa.*$", QRegularExpression::CaseInsensitiveOption);
161  QRegularExpression fullRegex(".*aaa.*", QRegularExpression::CaseInsensitiveOption);
162  QString containsStr("aaa");
163  number = 0;
164  timer.start();
165  for (const auto &str : std::as_const(strList1))
166  {
167  if (newRegex.match(str).hasMatch()) number++;
168  }
169  ms = timer.elapsed();
170  out << "new regex matched " << number << " of" << strList1.size() << " strings in " << ms << "ms" << Qt::endl;
171  number = 0;
172  timer.start();
173  for (const auto &str : std::as_const(strList2))
174  {
175  if (fullRegex.match(str).hasMatch()) number++;
176  }
177  ms = timer.elapsed();
178  out << "full regex matched " << number << " of" << strList2.size() << " strings in " << ms << "ms" << Qt::endl;
179  number = 0;
180  timer.start();
181  for (const auto &str : std::as_const(strList4))
182  {
183  if (str.contains(containsStr)) number++;
184  }
185  ms = timer.elapsed();
186  out << "contains matched " << number << " of " << strList4.size() << " strings in " << ms << "ms" << Qt::endl;
187 
188  out << "-----------------------------------------------" << Qt::endl;
189  return EXIT_SUCCESS;
190  }
191 
192  int CSamplesPerformance::interpolatorScenario(QTextStream &out, int numberOfCallsigns, int numberOfTimes)
193  {
194  const qint64 baseTimeEpoch = QDateTime::currentMSecsSinceEpoch();
195  CAircraftSituationList situations = createSituations(baseTimeEpoch, numberOfCallsigns, numberOfTimes);
196  CAircraftSituationList situationsBefore;
197  CAircraftSituationList situationsAfter;
198 
199  qint64 halfTime = baseTimeEpoch + DeltaTime * numberOfTimes / 2;
200 
201  QElapsedTimer timer;
202  timer.start();
203  for (int cs = 0; cs < numberOfCallsigns; cs++)
204  {
205  CCallsign callsign("CS" + QString::number(cs));
206  situationsBefore = situations.findBefore(halfTime).findByCallsign(callsign);
207  situationsAfter = situations.findAfter(halfTime - 1).findByCallsign(callsign);
208  }
209  out << "Reads by time, callsigns: " << timer.elapsed() << "ms" << Qt::endl;
210 
211  timer.start();
212  situationsBefore = situations.findBefore(halfTime);
213  situationsAfter = situations.findAfter(halfTime - 1);
214  for (int cs = 0; cs < numberOfCallsigns; cs++)
215  {
216  CCallsign callsign("CS" + QString::number(cs));
217  CAircraftSituationList csSituationsBefore = situationsBefore.findByCallsign(callsign);
218  CAircraftSituationList csSituationsAfter = situationsAfter.findByCallsign(callsign);
219  Q_UNUSED(csSituationsBefore);
220  Q_UNUSED(csSituationsAfter);
221  }
222  out << "Split by time upfront, then callsigns: " << timer.elapsed() << "ms" << Qt::endl;
223  int b = situationsBefore.size();
224  int a = situationsAfter.size();
225  Q_ASSERT(a + b == numberOfTimes * numberOfCallsigns);
226  Q_UNUSED(a);
227  Q_UNUSED(b);
228 
229  timer.start();
230  const QHash<CCallsign, CAircraftSituationList> csSituations = situations.splitPerCallsign();
231  out << "Split by " << csSituations.size() << " callsigns, " << timer.elapsed() << "ms" << Qt::endl;
232 
233  timer.start();
234  for (const CAircraftSituationList &csl : csSituations)
235  {
236  CAircraftSituationList csSituationsBefore = csl.findBefore(halfTime);
237  CAircraftSituationList csSituationsAfter = csl.findAfter(halfTime - 1);
238  a = csSituationsBefore.size();
239  b = csSituationsAfter.size();
240  Q_ASSERT(a + b == numberOfTimes);
241  Q_UNUSED(csSituationsBefore);
242  Q_UNUSED(csSituationsAfter);
243  }
244  out << "Split by callsign, by time: " << timer.elapsed() << "ms" << Qt::endl;
245 
246  out << Qt::endl;
247  return EXIT_SUCCESS;
248  }
249 
250  int CSamplesPerformance::samplesJson(QTextStream &out)
251  {
252  QElapsedTimer timer;
253  auto situations = createSituations(0, 10000, 10);
254  auto models = createModels(10000, 100);
255 
256  timer.start();
257  QJsonObject json = situations.toJson();
258  out << "Convert 100,000 aircraft situations to JSON: " << timer.elapsed() << "ms" << Qt::endl;
259 
260  timer.start();
261  situations.convertFromJson(json);
262  out << "Convert 100,000 aircraft situations from JSON: " << timer.elapsed() << "ms" << Qt::endl
263  << Qt::endl;
264 
265  timer.start();
266  json = models.toJson();
267  out << "Convert 10,000 aircraft models to JSON (naive): " << timer.elapsed() << "ms" << Qt::endl;
268 
269  timer.start();
270  models.convertFromJson(json);
271  out << "Convert 10,000 aircraft models from JSON (naive): " << timer.elapsed() << "ms" << Qt::endl
272  << Qt::endl;
273 
274  timer.start();
275  json = models.toMemoizedJson();
276  out << "Convert 10,000 aircraft models to JSON (memoize): " << timer.elapsed() << "ms" << Qt::endl;
277 
278  timer.start();
279  models.convertFromMemoizedJson(json);
280  out << "Convert 10,000 aircraft models from JSON (memoize): " << timer.elapsed() << "ms" << Qt::endl
281  << Qt::endl;
282 
283  return EXIT_SUCCESS;
284  }
285 
286  int CSamplesPerformance::samplesJsonModelAndLivery(QTextStream &out)
287  {
288  const QString dir = CSwiftDirectories::staticDbFilesDirectory();
289  const QString modelFileName = QDir(dir).filePath("models.json");
290  const QString liveriesFileName = QDir(dir).filePath("liveries.json");
291 
292  QFile modelFile(modelFileName);
293  Q_ASSERT_X(modelFile.exists(), Q_FUNC_INFO, "Model file does not exist");
294  QFile liveryFile(liveriesFileName);
295  Q_ASSERT_X(liveryFile.exists(), Q_FUNC_INFO, "Liveries file does not exist");
296 
297  out << "Loaded DB JSON model file " << modelFile.fileName() << Qt::endl;
298  const QString modelData = CFileUtils::readFileToString(modelFile.fileName());
299  Q_ASSERT_X(!modelData.isEmpty(), Q_FUNC_INFO, "Model file empty");
300 
301  out << "Loaded DB JSON livery file " << liveryFile.fileName() << Qt::endl;
302  const QString liveryData = CFileUtils::readFileToString(liveryFile.fileName());
303  Q_ASSERT_X(!liveryData.isEmpty(), Q_FUNC_INFO, "Livery file empty");
304 
305  // DB format, all models denormalized in DB JSON format
307  QElapsedTimer timer;
308 
309  CDatabaseReader::stringToDatastoreResponse(liveryData, response);
310  timer.start();
311  const CLiveryList dbLiveries = CLiveryList::fromDatabaseJson(response);
312  qint64 ms = timer.elapsed();
313  out << "Read via DB JSON format: " << dbLiveries.size() << " liveries in " << ms << "ms" << Qt::endl;
314 
315  // does not result in better performance, liveries/airlines have almost a 1:1 ratio
316  // unlike models' fromDatabaseJsonCaching not many airlines will be recycled
317  timer.start();
318  const CLiveryList dbLiveries2 = CLiveryList::fromDatabaseJsonCaching(response);
319  ms = timer.elapsed();
320  out << "Read via DB JSON format (new): " << dbLiveries2.size() << " liveries in " << ms << "ms" << Qt::endl;
321 
322  const CAirlineIcaoCodeList liveryAirlines = dbLiveries2.getAirlines();
323  timer.start();
324  const CLiveryList dbLiveries3 = CLiveryList::fromDatabaseJsonCaching(response, liveryAirlines);
325  ms = timer.elapsed();
326  out << "Read via DB JSON format (new, passing airlines): " << dbLiveries3.size() << " liveries in " << ms
327  << "ms" << Qt::endl;
328 
329  CDatabaseReader::stringToDatastoreResponse(modelData, response);
330  timer.start();
331  const CAircraftModelList dbModels = CAircraftModelList::fromDatabaseJson(response);
332  ms = timer.elapsed();
333  out << "Read via DB JSON format: " << dbModels.size() << " models in " << ms << "ms" << Qt::endl;
334 
335  timer.start();
336  const CAircraftModelList dbModels2 = CAircraftModelList::fromDatabaseJsonCaching(response);
337  ms = timer.elapsed();
338  out << "Read via DB JSON format (new): " << dbModels2.size() << " models in " << ms << "ms" << Qt::endl;
339 
340  // swift JSON format
341  const QJsonObject swiftJsonObject = dbModels.toJson();
342  out << "Converted to swift JSON" << Qt::endl;
343 
344  CAircraftModelList swiftModels;
345  timer.start();
346  swiftModels.convertFromJson(swiftJsonObject);
347  ms = timer.elapsed();
348  out << "Read via swift JSON format: " << swiftModels.size() << " models in " << ms << "ms" << Qt::endl;
349 
350  Q_ASSERT_X(swiftModels.size() == dbModels.size(), Q_FUNC_INFO, "Mismatching container size");
351  return EXIT_SUCCESS;
352  }
353 
354  int CSamplesPerformance::samplesStringUtilsVsRegEx(QTextStream &out)
355  {
356  QElapsedTimer timer;
357  static const QString chars =
358  "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789~_-=+!\"@#$%^&*()[]{} \t;:\\/?,.<>";
359  QStringList strings;
360  std::generate_n(std::back_inserter(strings), 100000, [] {
361  QString s;
362  std::generate_n(std::back_inserter(s), 10,
363  [] { return chars[CMathUtils::randomInteger(0, static_cast<int>(chars.size() - 1))]; });
364  return s;
365  });
366  QString bigString = strings.join("\n");
367 
368  QRegularExpression upperRegex("[A-Z]");
369  upperRegex.optimize();
370 
371  timer.start();
372  for (const QString &s : std::as_const(strings))
373  {
374  auto c = containsChar(s, [](QChar c) { return c.isUpper(); });
375  Q_UNUSED(c);
376  }
377  out << "Check 100,000 strings for containing uppercase letter: (utility) " << timer.elapsed() << "ms"
378  << Qt::endl;
379 
380  timer.start();
381  for (const QString &s : std::as_const(strings))
382  {
383  auto c = s.contains(upperRegex);
384  Q_UNUSED(c);
385  }
386  out << "Check 100,000 strings for containing uppercase letter: (regex) " << timer.elapsed() << "ms"
387  << Qt::endl
388  << Qt::endl;
389 
390  timer.start();
391  for (const QString &s : std::as_const(strings))
392  {
393  auto i = indexOfChar(s, [](QChar c) { return c.isUpper(); });
394  Q_UNUSED(i);
395  }
396  out << "Check 100,000 strings for index of first uppercase letter: (utility) " << timer.elapsed() << "ms"
397  << Qt::endl;
398 
399  timer.start();
400  for (const QString &s : std::as_const(strings))
401  {
402  auto i = s.indexOf(upperRegex);
403  Q_UNUSED(i);
404  }
405  out << "Check 100,000 strings for index of first uppercase letter: (regex) " << timer.elapsed() << "ms"
406  << Qt::endl
407  << Qt::endl;
408 
409  auto temp = strings;
410  timer.start();
411  for (QString &s : strings)
412  {
413  removeChars(s, [](QChar c) { return c.isUpper(); });
414  }
415  out << "Remove from 100,000 strings all uppercase letters: (utility) " << timer.elapsed() << "ms"
416  << Qt::endl;
417  strings = temp;
418 
419  timer.start();
420  for (QString &s : strings) { s.remove(upperRegex); }
421  out << "Remove from 100,000 strings all uppercase letters: (regex) " << timer.elapsed() << "ms"
422  << Qt::endl
423  << Qt::endl;
424 
425  timer.start();
426  {
427  auto lines = splitLines(bigString);
428  Q_UNUSED(lines);
429  }
430  out << "Split 100,000 line string into list of lines: (QStringList) " << timer.elapsed() << "ms"
431  << Qt::endl;
432 
433  timer.start();
434  {
435  auto lines = splitLinesRefs(bigString);
436  Q_UNUSED(lines);
437  }
438  out << "Split 100,000 line string into list of lines: (QList<QStringView>) " << timer.elapsed() << "ms"
439  << Qt::endl;
440 
441  return EXIT_SUCCESS;
442  }
443 
444  int CSamplesPerformance::samplesStringConcat(QTextStream &out)
445  {
446  const int loop = 250000;
447  QString x;
448  const QString x1 = "11-1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
449  const QString x2 = "22-1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
450  const QString x3 = "33-1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
451  const QString x4 = "44-1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
452  const QString x5 = "55-1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
453  const QString x6 = "66-1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
454  const QString x7 = "77-1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
455  const QString x8 = "88-1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
456  const QString x9 = "99-1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
457 
458  QElapsedTimer time;
459  time.start();
460  for (int i = 0; i < loop; i++) { x += "12-1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; }
461  out << "+= String " << time.elapsed() << "ms" << Qt::endl;
462  x.clear();
463 
464  time.start();
465  for (int i = 0; i < loop; i++)
466  {
467  x += QLatin1String("12-1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ");
468  }
469  out << "+= QLatin1String " << time.elapsed() << "ms" << Qt::endl;
470  x.clear();
471 
472  time.start();
473  for (int i = 0; i < loop; i++)
474  {
475  x += QStringLiteral("12-1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ");
476  }
477  out << "+= QStringLiteral " << time.elapsed() << "ms" << Qt::endl;
478  x.clear();
479 
480  time.start();
481  for (int i = 0; i < loop; i++) { x = x1 + x2 + x3 + x4 + x5 + x6 + x7 + x8 + x9; }
482  out << "+ String multiple " << time.elapsed() << "ms" << Qt::endl;
483  x.clear();
484 
485  time.start();
486  for (int i = 0; i < loop; i++) { x = x1 % x2 % x3 % x4 % x5 % x6 % x7 % x8 % x9; }
487  out << "% String multiple " << time.elapsed() << "ms" << Qt::endl;
488  x.clear();
489 
490  time.start();
491  for (int i = 0; i < loop; i++)
492  {
493  x = x.append(x1).append(x2).append(x3).append(x4).append(x5).append(x6).append(x7).append(x8).append(x9);
494  }
495  out << "append String multiple " << time.elapsed() << "ms" << Qt::endl;
496  x.clear();
497 
498  static const QString xArgString("%1 %2 %3 %4 %5 %6 %7 %8 %9");
499  time.start();
500  for (int i = 0; i < loop; i++) { x = xArgString.arg(x1, x2, x3, x4, x5, x6, x7, x8, x9); }
501  out << "arg String multiple " << time.elapsed() << "ms" << Qt::endl;
502  x.clear();
503 
504  time.start();
505  for (int i = 0; i < loop; i++)
506  {
507  x = QStringLiteral("%1 %2 %3 %4 %5 %6 %7 %8 %9").arg(x1, x2, x3, x4, x5, x6, x7, x8, x9);
508  }
509  out << "arg QStringLiteral multiple " << time.elapsed() << "ms" << Qt::endl;
510  x.clear();
511 
512  return EXIT_SUCCESS;
513  }
514 
515  int CSamplesPerformance::samplesStringLiteralVsConstQString(QTextStream &out)
516  {
517  const int loop = 1e7;
518  QElapsedTimer time;
519  QString x;
520  time.start();
521  for (int i = 0; i < loop; i++) { x = fooString(); }
522  out << "by constQString " << time.elapsed() << "ms" << Qt::endl;
523  x.clear();
524 
525  time.start();
526  for (int i = 0; i < loop; i++) { x = fooStringLiteral(); }
527  out << "by QStringLiteral " << time.elapsed() << "ms" << Qt::endl;
528  x.clear();
529 
530  time.start();
531  for (int i = 0; i < loop; i++)
532  {
533  x = QString("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent libero. "
534  "Sed cursus ante dapibus diam. Sed nisi.");
535  }
536  out << "by QString(\"...\") " << time.elapsed() << "ms" << Qt::endl;
537  x.clear();
538 
539  time.start();
540  for (int i = 0; i < loop; i++)
541  {
542  x = QStringLiteral("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent "
543  "libero. Sed cursus ante dapibus diam. Sed nisi.");
544  }
545  out << "by QStringLiteral(\"...\") " << time.elapsed() << "ms" << Qt::endl;
546  x.clear();
547 
548  time.start();
549  for (int i = 0; i < loop; i++)
550  {
551  QStringList foo = generateList();
552  Q_UNUSED(foo.size());
553  }
554  out << "generated list " << time.elapsed() << "ms" << Qt::endl;
555 
556  time.start();
557  for (int i = 0; i < loop; i++)
558  {
559  QStringList foo = replacedList();
560  Q_UNUSED(foo.size());
561  }
562  out << "replaced list " << time.elapsed() << "ms" << Qt::endl;
563 
564  return EXIT_SUCCESS;
565  }
566 
567  int CSamplesPerformance::sampleQMapVsQHashByCallsign(QTextStream &out)
568  {
569  const CCallsignSet cs10 = CSamplesPerformance::callsigns(10);
570  const CCallsignSet cs25 = CSamplesPerformance::callsigns(25);
571  const CCallsignSet cs50 = CSamplesPerformance::callsigns(50);
572 
573  const QMap<CCallsign, CAircraftSituation> m10 = CSamplesPerformance::situationsMap(cs10);
574  const QMap<CCallsign, CAircraftSituation> m25 = CSamplesPerformance::situationsMap(cs25);
575  const QMap<CCallsign, CAircraftSituation> m50 = CSamplesPerformance::situationsMap(cs50);
576 
577  const QHash<CCallsign, CAircraftSituation> h10 = CSamplesPerformance::situationsHash(cs10);
578  const QHash<CCallsign, CAircraftSituation> h25 = CSamplesPerformance::situationsHash(cs25);
579  const QHash<CCallsign, CAircraftSituation> h50 = CSamplesPerformance::situationsHash(cs50);
580 
581  Q_ASSERT(m10.size() == 10 && h10.size() == 10);
582  Q_ASSERT(m25.size() == 25 && h25.size() == 25);
583  Q_ASSERT(m50.size() == 50 && h50.size() == 50);
584 
585  // QList since we have to add callsigns multiple times, set does not allow that
586  QList<CCallsign> cs_10_100_rnd;
587  QList<CCallsign> cs_25_100_rnd;
588  QList<CCallsign> cs_50_100_rnd;
589 
590  for (int i = 0; i < 20; ++i)
591  {
592  cs_10_100_rnd.append(cs10.randomElements(5).toQList());
593  cs_25_100_rnd.append(cs25.randomElements(5).toQList());
594  cs_50_100_rnd.append(cs50.randomElements(5).toQList());
595  }
596 
597  Q_ASSERT(cs_10_100_rnd.size() == 100);
598  Q_ASSERT(cs_25_100_rnd.size() == 100);
599  Q_ASSERT(cs_50_100_rnd.size() == 100);
600 
601  QElapsedTimer time;
602  time.start();
603  for (int i = 1; i < 10000; ++i)
604  {
605  for (const CCallsign &cs : cs_10_100_rnd)
606  {
607  CAircraftSituation s = m10[cs];
608  Q_ASSERT_X(s.getCallsign() == cs, Q_FUNC_INFO, "Wromg callsign");
609  }
610  }
611  out << "map 100 out of 10: " << time.elapsed() << "ms" << Qt::endl;
612 
613  time.start();
614  for (int i = 1; i < 10000; ++i)
615  {
616  for (const CCallsign &cs : cs_10_100_rnd)
617  {
618  CAircraftSituation s = h10[cs];
619  Q_ASSERT_X(s.getCallsign() == cs, Q_FUNC_INFO, "Wromg callsign");
620  }
621  }
622  out << "hash 100 out of 10: " << time.elapsed() << "ms" << Qt::endl;
623 
624  time.start();
625  for (int i = 1; i < 10000; ++i)
626  {
627  for (const CCallsign &cs : cs_25_100_rnd)
628  {
629  CAircraftSituation s = m25[cs];
630  Q_ASSERT_X(s.getCallsign() == cs, Q_FUNC_INFO, "Wromg callsign");
631  }
632  }
633  out << "map 100 out of 25: " << time.elapsed() << "ms" << Qt::endl;
634 
635  time.start();
636  for (int i = 1; i < 10000; ++i)
637  {
638  for (const CCallsign &cs : cs_25_100_rnd)
639  {
640  CAircraftSituation s = h25[cs];
641  Q_ASSERT_X(s.getCallsign() == cs, Q_FUNC_INFO, "Wromg callsign");
642  }
643  }
644  out << "hash 100 out of 25: " << time.elapsed() << "ms" << Qt::endl;
645 
646  time.start();
647  for (int i = 1; i < 10000; ++i)
648  {
649  for (const CCallsign &cs : cs_50_100_rnd)
650  {
651  CAircraftSituation s = m50[cs];
652  Q_ASSERT_X(s.getCallsign() == cs, Q_FUNC_INFO, "Wromg callsign");
653  }
654  }
655  out << "map 100 out of 50: " << time.elapsed() << "ms" << Qt::endl;
656 
657  time.start();
658  for (int i = 1; i < 10000; ++i)
659  {
660  for (const CCallsign &cs : cs_50_100_rnd)
661  {
662  CAircraftSituation s = h50[cs];
663  Q_ASSERT_X(s.getCallsign() == cs, Q_FUNC_INFO, "Wromg callsign");
664  }
665  }
666  out << "hash 100 out of 50: " << time.elapsed() << "ms" << Qt::endl;
667 
668  return EXIT_SUCCESS;
669  }
670 
671  CAircraftSituationList CSamplesPerformance::createSituations(qint64 baseTimeEpoch, int numberOfCallsigns,
672  int numberOfTimes)
673  {
674  CAircraftSituationList situations;
675  for (int cs = 0; cs < numberOfCallsigns; cs++)
676  {
677  CCallsign callsign("CS" + QString::number(cs));
678  CCoordinateGeodetic coordinate(cs, cs, cs);
679  for (int t = 0; t < numberOfTimes; t++)
680  {
681  CAircraftSituation s(callsign, coordinate);
682  s.setMSecsSinceEpoch(baseTimeEpoch + DeltaTime * t);
683  situations.push_back(s);
684  }
685  }
686  return situations;
687  }
688 
689  CAircraftModelList CSamplesPerformance::createModels(int numberOfModels, int numberOfMemoParts)
690  {
691  CAircraftIcaoCodeList aircraftIcaos;
692  CLiveryList liveries;
693  CDistributorList distributors;
694  for (int i = 0; i < numberOfMemoParts; ++i)
695  {
696  aircraftIcaos.push_back(CAircraftIcaoCode("A" + QString::number(i), "A" + QString::number(i), "L1P", "Lego",
697  "Foo", CWakeTurbulenceCategory::MEDIUM, false, false, false, 0));
698  liveries.push_back(CLivery(
699  "A" + QString::number(i),
700  CAirlineIcaoCode("A" + QString::number(i), "Foo", CCountry("DE", "Germany"), "Foo", false, false),
701  "Foo", "red", "blue", false));
702  distributors.push_back(CDistributor(QString::number(i), "Foo", {}, {}, CSimulatorInfo::FSX));
703  }
704 
705  CAircraftModelList models;
706  for (int i = 0; i < numberOfModels; ++i)
707  {
708  const auto &aircraftIcao = aircraftIcaos[CMathUtils::randomInteger(0, numberOfMemoParts - 1)];
709  const auto &livery = liveries[CMathUtils::randomInteger(0, numberOfMemoParts - 1)];
710  const auto &distributor = distributors[CMathUtils::randomInteger(0, numberOfMemoParts - 1)];
711  models.push_back(CAircraftModel(QString::number(i), CAircraftModel::TypeUnknown, CSimulatorInfo::FSX,
712  QString::number(i), QString::number(i), aircraftIcao, livery));
713  models.back().setDistributor(distributor);
714  }
715  return models;
716  }
717 
718  void CSamplesPerformance::calculateDistance(int n)
719  {
720  if (n < 1) { return; }
721  CAtcStation atc = CTesting::createStation(1);
722  const QList<CCoordinateGeodetic> pos(
723  { CCoordinateGeodetic(10.0, 10.0, 10.0), CCoordinateGeodetic(20.0, 20.0, 20.0),
724  CCoordinateGeodetic(30.0, 30.0, 30.0), CCoordinateGeodetic(40.0, 40.0, 40.0),
725  CCoordinateGeodetic(50.0, 50.0, 50.0), CCoordinateGeodetic(60.0, 60.0, 60.0),
726  CCoordinateGeodetic(70.0, 70.0, 70.0) });
727  const qsizetype s = pos.size();
728  for (int i = 0; i < n; i++)
729  {
730  const qsizetype p = i % s;
732  }
733  }
734 
735  void CSamplesPerformance::copy10kStations(int times)
736  {
737  CAtcStationList stations;
738  for (int i = 0; i < times; i++)
739  {
740  stations = stations10k();
741  stations.pop_back(); // make sure stations are really copied (copy-on-write)
742  }
743  }
744 
745  void CSamplesPerformance::parseWgs(int times)
746  {
747  static QStringList wgsLatLng({ "12° 11′ 10″ N", "11° 22′ 33″ W", "48° 21′ 13″ N", "11° 47′ 09″ E",
748  " 8° 21′ 13″ N", "11° 47′ 09″ W", "18° 21′ 13″ S", "11° 47′ 09″ E",
749  "09° 12′ 13″ S", "11° 47′ 09″ W" });
750 
752  const CAltitude a(333, CLengthUnit::m());
753  for (int i = 0; i < times; i++)
754  {
755  int idx = (i % 5) * 2;
756  c = CCoordinateGeodetic::fromWgs84(wgsLatLng.at(idx), wgsLatLng.at(idx + 1), a);
757  }
758  }
759 
760  const QString &CSamplesPerformance::fooString()
761  {
762  static const QString s("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent "
763  "libero. Sed cursus ante dapibus diam. Sed nisi.");
764  return s;
765  }
766 
767  QString CSamplesPerformance::fooStringLiteral()
768  {
769  return QStringLiteral("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent "
770  "libero. Sed cursus ante dapibus diam. Sed nisi.");
771  }
772 
773  QStringList CSamplesPerformance::generateList() { return QStringList({ "1", "2", "3", "4" }); }
774 
775  QStringList CSamplesPerformance::replacedList()
776  {
777  static const QStringList l({ "1", "2", "3", "4" });
778  QStringList lc(l);
779  lc[1] = QStringLiteral("6");
780  lc[3] = QStringLiteral("7");
781  return lc;
782  }
783 
784  CCallsignSet CSamplesPerformance::callsigns(int number)
785  {
786  CCallsignSet set;
787  static const QString cs("FOO%1");
788  for (int i = 0; i < number; i++) { set.insert(CCallsign(cs.arg(i))); }
789  return set;
790  }
791 
792  QMap<CCallsign, CAircraftSituation> CSamplesPerformance::situationsMap(const CCallsignSet &callsigns)
793  {
795  for (const CCallsign &cs : callsigns)
796  {
797  const CAircraftSituation s(cs);
798  situations.insert(cs, s);
799  }
800  return situations;
801  }
802 
803  QHash<CCallsign, CAircraftSituation> CSamplesPerformance::situationsHash(const CCallsignSet &callsigns)
804  {
806  for (const CCallsign &cs : callsigns)
807  {
808  const CAircraftSituation s(cs);
809  situations.insert(cs, s);
810  }
811  return situations;
812  }
813 
814  const CAtcStationList &CSamplesPerformance::stations10k()
815  {
816  static const CAtcStationList s = CTesting::createAtcStations(10000, false);
817  return s;
818  }
819 
820  void CSamplesPerformance::accessStationsData(const CAtcStationList &stations, bool byPropertyIndex)
821  {
822  for (const CAtcStation &station : stations)
823  {
824  const QString s = CSamplesPerformance::accessStationData(station, byPropertyIndex);
825  Q_UNUSED(s);
826  }
827  }
828 
829  QString CSamplesPerformance::accessStationData(const CAtcStation &station, bool byPropertyIndex)
830  {
831  QString r;
832  if (byPropertyIndex)
833  {
834  r.append(station.propertyByIndex(CPropertyIndex { CAtcStation::IndexCallsign, CCallsign::IndexString })
835  .toString());
836  r.append(station.propertyByIndex(CPropertyIndex { CAtcStation::IndexController, CUser::IndexRealName })
837  .toString());
838  r.append(station
839  .propertyByIndex(
840  CPropertyIndex { CAtcStation::IndexPosition, CCoordinateGeodetic::IndexLatitudeAsString })
841  .toString());
842  r.append(station
843  .propertyByIndex(
844  CPropertyIndex { CAtcStation::IndexPosition, CCoordinateGeodetic::IndexLongitudeAsString })
845  .toString());
846  r.append(station
847  .propertyByIndex(CPropertyIndex { CAtcStation::IndexRelativeDistance,
848  CLength::IndexValueRounded2DigitsWithUnit })
849  .toString());
850  r.append(station.propertyByIndex(CPropertyIndex { CAtcStation::IndexLogoffTime })
851  .toDateTime()
852  .toString("yyyy-MM-dd hh:mm"));
853  }
854  else
855  {
856  r.append(station.getCallsignAsString());
857  r.append(station.getController().getRealName());
858  r.append(station.getPosition().latitudeAsString());
859  r.append(station.getPosition().longitudeAsString());
860  r.append(station.getRelativeDistance().toQString(true));
861  r.append(station.getLogoffTimeUtc().toString("yyyy-MM-dd hh:mm"));
862  }
863  return r;
864  }
865 } // 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:74
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:34
SWIFT_MISC_EXPORT QList< QStringView > splitLinesRefs(const QString &s)
Split a string into multiple lines. Blank lines are skipped.
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:77
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:64
Response from our database (depending on JSON DB backend generates)