From 978fe5eea2ef3445dc48b7d7a064696f326eea8c Mon Sep 17 00:00:00 2001 From: Klaus Basan Date: Wed, 13 Jun 2018 16:36:30 +0200 Subject: [PATCH] Ref T275, Ref T280 hint for timestamp based list if they are already sorted --- .../simulation/remoteaircraftprovider.cpp | 6 ++- src/blackmisc/timestampobjectlist.cpp | 52 ++++++++++++++++--- src/blackmisc/timestampobjectlist.h | 33 ++++++++++-- tests/blackmisc/testaircraftsituation.cpp | 32 ++++++++++++ tests/blackmisc/testaircraftsituation.h | 3 ++ tests/blackmisc/testcontainers.cpp | 2 +- 6 files changed, 116 insertions(+), 12 deletions(-) diff --git a/src/blackmisc/simulation/remoteaircraftprovider.cpp b/src/blackmisc/simulation/remoteaircraftprovider.cpp index e3dfb20ad..afa45cbfc 100644 --- a/src/blackmisc/simulation/remoteaircraftprovider.cpp +++ b/src/blackmisc/simulation/remoteaircraftprovider.cpp @@ -219,7 +219,8 @@ namespace BlackMisc else { situationList.front().transferGroundElevation(situationCorrected); // transfer last situation if possible - situationList.push_frontKeepLatestFirstAdjustOffset(situationCorrected, IRemoteAircraftProvider::MaxSituationsPerCallsign); + situationList.push_frontKeepLatestFirstAdjustOffset(situationCorrected, true, IRemoteAircraftProvider::MaxSituationsPerCallsign); + situationList.setAdjustedSortHint(CAircraftSituationList::AdjustedTimestampLatestFirst); // unify all inbound ground information if (situation.hasInboundGroundDetails()) @@ -252,7 +253,8 @@ namespace BlackMisc m_partsAdded++; m_partsLastModified[callsign] = ts; CAircraftPartsList &partsList = m_partsByCallsign[callsign]; - partsList.push_frontKeepLatestFirstAdjustOffset(parts, IRemoteAircraftProvider::MaxPartsPerCallsign); + partsList.push_frontKeepLatestFirstAdjustOffset(parts, true, IRemoteAircraftProvider::MaxPartsPerCallsign); + partsList.setAdjustedSortHint(CAircraftPartsList::AdjustedTimestampLatestFirst); // remove outdated parts (but never remove the most recent one) if (removeOutdated) { IRemoteAircraftProvider::removeOutdatedParts(partsList); } diff --git a/src/blackmisc/timestampobjectlist.cpp b/src/blackmisc/timestampobjectlist.cpp index f8d7bef39..533080141 100644 --- a/src/blackmisc/timestampobjectlist.cpp +++ b/src/blackmisc/timestampobjectlist.cpp @@ -253,14 +253,21 @@ namespace BlackMisc template void ITimestampObjectList::sortOldestFirst() { - this->container().sort(BlackMisc::Predicates::MemberLess(&OBJ::getMSecsSinceEpoch)); + this->container().sort(Predicates::MemberLess(&OBJ::getMSecsSinceEpoch)); } template - void ITimestampObjectList::push_frontKeepLatestFirst(const OBJ &value, int maxElements) + void ITimestampObjectList::push_frontKeepLatestFirst(const OBJ &value, bool replaceSameTimestamp, int maxElements) { Q_ASSERT_X(maxElements < 0 || maxElements > 1, Q_FUNC_INFO, "Max.value wrong range"); CONTAINER &c = this->container(); + if (replaceSameTimestamp && !c.isEmpty() && c[0].getMSecsSinceEpoch() == value.getMSecsSinceEpoch()) + { + c[0] = value; + if (maxElements > 0) { c.truncate(maxElements); } + return; + } + if (maxElements > 0) { c.truncate(maxElements - 1); } const bool needSort = !c.isEmpty() && value.isOlderThan(c.front()); c.push_front(value); @@ -270,6 +277,21 @@ namespace BlackMisc } } + template + int ITimestampObjectList::replaceIfSameTimestamp(const OBJ &newObject) + { + int c = 0; + for (OBJ &obj : this->container()) + { + if (obj.getMSecsSinceEpoch() == newObject.getMSecsSinceEpoch()) + { + obj = newObject; + c++; + } + } + return c; + } + template bool ITimestampObjectList::isSortedLatestLast() const { @@ -374,10 +396,17 @@ namespace BlackMisc } template - void ITimestampWithOffsetObjectList::push_frontKeepLatestAdjustedFirst(const OBJ &value, int maxElements) + void ITimestampWithOffsetObjectList::push_frontKeepLatestAdjustedFirst(const OBJ &value, bool replaceSameTimestamp, int maxElements) { Q_ASSERT_X(maxElements < 0 || maxElements > 1, Q_FUNC_INFO, "Max.value wrong range"); CONTAINER &c = this->container(); + if (replaceSameTimestamp && !c.isEmpty() && c[0].getMSecsSinceEpoch() == value.getMSecsSinceEpoch()) + { + c[0] = value; + if (maxElements > 0) { c.truncate(maxElements); } + return; + } + if (maxElements > 0) { c.truncate(maxElements - 1); } const bool needSort = !c.isEmpty() && value.isOlderThanAdjusted(c.front()); c.push_front(value); @@ -388,9 +417,9 @@ namespace BlackMisc } template - void ITimestampWithOffsetObjectList::push_frontKeepLatestFirstAdjustOffset(const OBJ &value, int maxElements) + void ITimestampWithOffsetObjectList::push_frontKeepLatestFirstAdjustOffset(const OBJ &value, bool replaceSameTimestamp, int maxElements) { - ITimestampObjectList::push_frontKeepLatestFirst(value, maxElements); + ITimestampWithOffsetObjectList::push_frontKeepLatestAdjustedFirst(value, replaceSameTimestamp, maxElements); // now sorted by timestamp // this reflects normally the incoming order @@ -432,6 +461,7 @@ namespace BlackMisc copy.addMsecs(os * i); this->container().push_back(copy); } + this->setAdjustedSortHint(ITimestampWithOffsetObjectList::AdjustedTimestampLatestFirst); } template @@ -513,6 +543,10 @@ namespace BlackMisc OBJ ITimestampWithOffsetObjectList::latestAdjustedObject() const { if (this->container().isEmpty()) { return OBJ(); } + if (m_tsAdjustedSortHint == AdjustedTimestampLatestFirst) + { + return this->container().front(); + } const auto latest = std::max_element(this->container().begin(), this->container().end(), [](const OBJ & a, const OBJ & b) { return a.getAdjustedMSecsSinceEpoch() < b.getAdjustedMSecsSinceEpoch(); }); return *latest; } @@ -521,6 +555,10 @@ namespace BlackMisc OBJ ITimestampWithOffsetObjectList::oldestAdjustedObject() const { if (this->container().isEmpty()) { return OBJ(); } + if (m_tsAdjustedSortHint == AdjustedTimestampLatestFirst) + { + return this->container().back(); + } const auto oldest = std::min_element(this->container().begin(), this->container().end(), [](const OBJ & a, const OBJ & b) { return a.getAdjustedMSecsSinceEpoch() < b.getAdjustedMSecsSinceEpoch(); }); return *oldest; } @@ -535,7 +573,8 @@ namespace BlackMisc template qint64 ITimestampWithOffsetObjectList::latestAdjustedTimestampMsecsSinceEpoch() const { - const QDateTime dt(this->latestTimestamp()); + if (this->container().isEmpty()) { return -1; } + const QDateTime dt(this->latestAdjustedTimestamp()); return dt.isValid() ? dt.toMSecsSinceEpoch() : -1; } @@ -549,6 +588,7 @@ namespace BlackMisc template qint64 ITimestampWithOffsetObjectList::oldestAdjustedTimestampMsecsSinceEpoch() const { + if (this->container().isEmpty()) { return -1; } const QDateTime dt(oldestAdjustedTimestamp()); return dt.isValid() ? dt.toMSecsSinceEpoch() : -1; } diff --git a/src/blackmisc/timestampobjectlist.h b/src/blackmisc/timestampobjectlist.h index 1c54bb1d2..659b10665 100644 --- a/src/blackmisc/timestampobjectlist.h +++ b/src/blackmisc/timestampobjectlist.h @@ -26,6 +26,13 @@ namespace BlackMisc template class ITimestampObjectList { public: + //! Hint if the list is sorted + enum HintTimestampSort + { + NoTimestampSortHint, + TimestampLatestFirst + }; + //! List of objects before dateTime (older) CONTAINER findBefore(const QDateTime &dateTime) const; @@ -102,7 +109,10 @@ namespace BlackMisc void sortOldestFirst(); //! Insert as first element by keeping maxElements and the latest first - void push_frontKeepLatestFirst(const OBJ &value, int maxElements = -1); + void push_frontKeepLatestFirst(const OBJ &value, bool replaceSameTimestamp = true, int maxElements = -1); + + //! Replace if an object has the same timestamp + int replaceIfSameTimestamp(const OBJ &newObject); //! Is completely sorted: latest last //! \remark all object must have a valid timestamp @@ -115,6 +125,9 @@ namespace BlackMisc //! Adds a time to all values void addMsecs(qint64 msToAdd); + //! Set the hint + void setSortHint(HintTimestampSort hint) { m_tsSortHint = hint; } + protected: //! Constructor ITimestampObjectList(); @@ -124,6 +137,8 @@ namespace BlackMisc //! Container CONTAINER &container(); + + HintTimestampSort m_tsSortHint = NoTimestampSortHint; //!< sort hint }; //! List of objects with timestamp and offset. @@ -131,6 +146,13 @@ namespace BlackMisc template class ITimestampWithOffsetObjectList : public ITimestampObjectList { public: + //! Hint if the list is sorted + enum HintAdjustedTimestampSort + { + NoAdjustedTimestampSortHint, + AdjustedTimestampLatestFirst + }; + //! Sort by adjusted timestamp void sortAdjustedLatestFirst(); @@ -153,11 +175,11 @@ namespace BlackMisc void addMsecsToOffset(qint64 msToAdd); //! Insert as first element by keeping maxElements and the latest first - void push_frontKeepLatestAdjustedFirst(const OBJ &value, int maxElements = -1); + void push_frontKeepLatestAdjustedFirst(const OBJ &value, bool replaceSameTimestamp = true, int maxElements = -1); //! Insert as first element by keeping maxElements and the latest first //! \remark adjust offset to average offset of two adjacent elements so adjusted values are sorted - void push_frontKeepLatestFirstAdjustOffset(const OBJ &value, int maxElements = -1); + void push_frontKeepLatestFirstAdjustOffset(const OBJ &value, bool replaceSameTimestamp = true, int maxElements = -1); //! Prefill with elements void prefillLatestAdjustedFirst(const OBJ &value, int elements, qint64 deltaTimeMs = -1); @@ -203,9 +225,14 @@ namespace BlackMisc //! Oldest adjusted timestamp qint64 oldestAdjustedTimestampMsecsSinceEpoch() const; + //! Set the hint + void setAdjustedSortHint(HintAdjustedTimestampSort hint) { m_tsAdjustedSortHint = hint; } + protected: //! Constructor ITimestampWithOffsetObjectList(); + + HintAdjustedTimestampSort m_tsAdjustedSortHint = NoAdjustedTimestampSortHint; //!< sort hint }; //! \cond PRIVATE diff --git a/tests/blackmisc/testaircraftsituation.cpp b/tests/blackmisc/testaircraftsituation.cpp index 9df10de83..d6474856a 100644 --- a/tests/blackmisc/testaircraftsituation.cpp +++ b/tests/blackmisc/testaircraftsituation.cpp @@ -20,7 +20,9 @@ #include "blackmisc/math/mathutils.h" #include +#include #include +#include using namespace BlackMisc::Aviation; using namespace BlackMisc::PhysicalQuantities; @@ -184,6 +186,36 @@ namespace BlackMiscTest QVERIFY2(corAlt == (ep.getAltitude() + cg), "Expect correction by CG"); } + void CTestAircraftSituation::sortHint() + { + CAircraftSituationList situations = testSituations(); + situations.sortAdjustedLatestFirst(); + + constexpr int Max = 500000; + QTime time; + time.start(); + for (int i = 0; i < Max; ++i) + { + CAircraftSituation s1 = situations.oldestAdjustedObject(); + CAircraftSituation s2 = situations.latestAdjustedObject(); + QVERIFY(s1.getAdjustedMSecsSinceEpoch() < s2.getAdjustedMSecsSinceEpoch()); + } + const int noHint = time.elapsed(); + situations.setAdjustedSortHint(CAircraftSituationList::AdjustedTimestampLatestFirst); + + time.start(); + for (int i = 0; i < Max; ++i) + { + CAircraftSituation s1 = situations.oldestAdjustedObject(); + CAircraftSituation s2 = situations.latestAdjustedObject(); + QVERIFY(s1.getAdjustedMSecsSinceEpoch() < s2.getAdjustedMSecsSinceEpoch()); + } + const int hint = time.elapsed(); + qDebug() << "Access without hint" << noHint << "ms"; + qDebug() << "Access with hint" << hint << "ms"; + QVERIFY2(hint < noHint, "Expected hinted sort being faster"); + } + CAircraftSituationList CTestAircraftSituation::testSituations() { // "Kugaaruk Airport","Pelly Bay","Canada","YBB","CYBB",68.534401,-89.808098,56,-7,"A","America/Edmonton","airport","OurAirports" diff --git a/tests/blackmisc/testaircraftsituation.h b/tests/blackmisc/testaircraftsituation.h index 7c39c48f0..ec1ff19ce 100644 --- a/tests/blackmisc/testaircraftsituation.h +++ b/tests/blackmisc/testaircraftsituation.h @@ -55,6 +55,9 @@ namespace BlackMiscTest //! Altitude correction void altitudeCorrection(); + //! Using sort hint + void sortHint(); + private: //! Test situations (ascending) static BlackMisc::Aviation::CAircraftSituationList testSituations(); diff --git a/tests/blackmisc/testcontainers.cpp b/tests/blackmisc/testcontainers.cpp index 4f67bb719..89ae453e5 100644 --- a/tests/blackmisc/testcontainers.cpp +++ b/tests/blackmisc/testcontainers.cpp @@ -329,7 +329,7 @@ namespace BlackMiscTest } ts += dt; - situations.push_frontKeepLatestFirstAdjustOffset(s, max); + situations.push_frontKeepLatestFirstAdjustOffset(s, true, max); QVERIFY2(situations.size() <= max, "Wrong size"); QVERIFY2(situations.isSortedAdjustedLatestFirstWithoutNullPositions(), "Wrong sort order");