diff --git a/src/plugins/qmlprofiler/qmlprofilerstatisticsmodel.cpp b/src/plugins/qmlprofiler/qmlprofilerstatisticsmodel.cpp index f4b9c931fae..a28647cbd0f 100644 --- a/src/plugins/qmlprofiler/qmlprofilerstatisticsmodel.cpp +++ b/src/plugins/qmlprofiler/qmlprofilerstatisticsmodel.cpp @@ -52,16 +52,16 @@ QString QmlProfilerStatisticsModel::nameForType(RangeType typeNumber) double QmlProfilerStatisticsModel::durationPercent(int typeId) const { - const QmlEventStats &global = m_data[-1]; - const QmlEventStats &stats = m_data[typeId]; - return double(stats.duration - stats.durationRecursive) / double(global.duration) * 100l; + return (typeId >= 0) + ? double(m_data[typeId].totalNonRecursive()) / double(m_rootDuration) * 100 + : 100; } double QmlProfilerStatisticsModel::durationSelfPercent(int typeId) const { - const QmlEventStats &global = m_data[-1]; - const QmlEventStats &stats = m_data[typeId]; - return double(stats.durationSelf) / double(global.duration) * 100l; + return (typeId >= 0) + ? (double(m_data[typeId].self) / double(m_rootDuration) * 100) + : 0; } QmlProfilerStatisticsModel::QmlProfilerStatisticsModel(QmlProfilerModelManager *modelManager) @@ -122,7 +122,8 @@ bool QmlProfilerStatisticsModel::isRestrictedToRange() const return m_modelManager->isRestrictedToRange(); } -const QHash &QmlProfilerStatisticsModel::getData() const +const QVector & +QmlProfilerStatisticsModel::getData() const { return m_data; } @@ -193,11 +194,11 @@ QString QmlProfilerStatisticsModel::summary(const QVector &typeIds) const void QmlProfilerStatisticsModel::clear() { + m_rootDuration = 0; m_data.clear(); m_notes.clear(); m_callStack.clear(); m_compileStack.clear(); - m_durations.clear(); if (!m_calleesModel.isNull()) m_calleesModel->clear(); if (!m_callersModel.isNull()) @@ -256,42 +257,40 @@ void QmlProfilerStatisticsModel::loadEvent(const QmlEvent &event, const QmlEvent if (!m_acceptedTypes.contains(type.rangeType())) return; + const int typeIndex = event.typeIndex(); bool isRecursive = false; QStack &stack = type.rangeType() == Compiling ? m_compileStack : m_callStack; switch (event.rangeStage()) { case RangeStart: stack.push(event); + if (m_data.length() <= typeIndex) + m_data.resize(m_modelManager->numLoadedEventTypes()); break; case RangeEnd: { // update stats QTC_ASSERT(!stack.isEmpty(), return); - QTC_ASSERT(stack.top().typeIndex() == event.typeIndex(), return); - QmlEventStats *stats = &m_data[event.typeIndex()]; + QTC_ASSERT(stack.top().typeIndex() == typeIndex, return); + QmlEventStats &stats = m_data[typeIndex]; qint64 duration = event.timestamp() - stack.top().timestamp(); - stats->duration += duration; - stats->durationSelf += duration; - if (duration < stats->minTime) - stats->minTime = duration; - if (duration > stats->maxTime) - stats->maxTime = duration; - stats->calls++; - // for median computing - m_durations[event.typeIndex()].append(duration); + stats.total += duration; + stats.self += duration; + stats.durations.push_back(duration); stack.pop(); // recursion detection: check whether event was already in stack for (int ii = 0; ii < stack.size(); ++ii) { - if (stack.at(ii).typeIndex() == event.typeIndex()) { + if (stack.at(ii).typeIndex() == typeIndex) { isRecursive = true; - stats->durationRecursive += duration; + stats.recursive += duration; break; } } if (!stack.isEmpty()) - m_data[stack.top().typeIndex()].durationSelf -= duration; + m_data[stack.top().typeIndex()].self -= duration; else - m_data[-1].duration += duration; + m_rootDuration += duration; + break; } default: @@ -307,20 +306,8 @@ void QmlProfilerStatisticsModel::loadEvent(const QmlEvent &event, const QmlEvent void QmlProfilerStatisticsModel::finalize() { - // post-process: calc mean time, median time, percentoftime - for (QHash::iterator it = m_data.begin(); it != m_data.end(); ++it) { - QVector eventDurations = m_durations[it.key()]; - if (!eventDurations.isEmpty()) { - Utils::sort(eventDurations); - it->medianTime = eventDurations.at(eventDurations.count()/2); - } - } - - // insert root event - QmlEventStats &rootEvent = m_data[-1]; - rootEvent.minTime = rootEvent.maxTime = rootEvent.medianTime = rootEvent.duration; - rootEvent.durationSelf = 0; - rootEvent.calls = 1; + for (QmlEventStats &stats : m_data) + stats.finalize(); emit dataAvailable(); } @@ -346,15 +333,15 @@ QmlProfilerStatisticsRelativesModel::QmlProfilerStatisticsRelativesModel( this, &QmlProfilerStatisticsRelativesModel::dataAvailable); } -const QmlProfilerStatisticsRelativesModel::QmlStatisticsRelativesMap & +const QVector & QmlProfilerStatisticsRelativesModel::getData(int typeId) const { - QHash ::ConstIterator it = m_data.find(typeId); + auto it = m_data.find(typeId); if (it != m_data.end()) { return it.value(); } else { - static const QmlStatisticsRelativesMap emptyMap; - return emptyMap; + static const QVector emptyVector; + return emptyVector; } } @@ -363,6 +350,18 @@ const QVector &QmlProfilerStatisticsRelativesModel::getTypes() con return m_modelManager->eventTypes(); } +bool operator<(const QmlProfilerStatisticsRelativesModel::QmlStatisticsRelativesData &a, + const QmlProfilerStatisticsRelativesModel::QmlStatisticsRelativesData &b) +{ + return a.typeIndex < b.typeIndex; +} + +bool operator<(const QmlProfilerStatisticsRelativesModel::QmlStatisticsRelativesData &a, + int typeIndex) +{ + return a.typeIndex < typeIndex; +} + void QmlProfilerStatisticsRelativesModel::loadEvent(RangeType type, const QmlEvent &event, bool isRecursive) { @@ -379,15 +378,16 @@ void QmlProfilerStatisticsRelativesModel::loadEvent(RangeType type, const QmlEve int selfTypeIndex = (m_relation == QmlProfilerStatisticsCallers) ? event.typeIndex() : callerTypeIndex; - QmlStatisticsRelativesMap &relativesMap = m_data[selfTypeIndex]; - QmlStatisticsRelativesMap::Iterator it = relativesMap.find(relativeTypeIndex); - if (it != relativesMap.end()) { + QVector &relatives = m_data[selfTypeIndex]; + auto it = std::lower_bound(relatives.begin(), relatives.end(), relativeTypeIndex); + if (it != relatives.end() && it->typeIndex == relativeTypeIndex) { it->calls++; it->duration += event.timestamp() - stack.top().startTime; it->isRecursive = isRecursive || it->isRecursive; } else { - relativesMap.insert(relativeTypeIndex, QmlStatisticsRelativesData( - event.timestamp() - stack.top().startTime, 1, isRecursive)); + relatives.insert(it, QmlStatisticsRelativesData( + event.timestamp() - stack.top().startTime, 1, relativeTypeIndex, + isRecursive)); } stack.pop(); break; diff --git a/src/plugins/qmlprofiler/qmlprofilerstatisticsmodel.h b/src/plugins/qmlprofiler/qmlprofilerstatisticsmodel.h index 805df505482..673bf23372b 100644 --- a/src/plugins/qmlprofiler/qmlprofilerstatisticsmodel.h +++ b/src/plugins/qmlprofiler/qmlprofilerstatisticsmodel.h @@ -30,6 +30,8 @@ #include "qmleventlocation.h" #include "qmlprofilerconstants.h" +#include + #include #include #include @@ -52,13 +54,46 @@ public: static QString nameForType(RangeType typeNumber); struct QmlEventStats { - qint64 duration = 0; - qint64 durationSelf = 0; - qint64 durationRecursive = 0; + std::vector durations; + qint64 total = 0; + qint64 self = 0; + qint64 recursive = 0; + qint64 minimum = 0; + qint64 maximum = 0; + qint64 median = 0; qint64 calls = 0; - qint64 minTime = std::numeric_limits::max(); - qint64 maxTime = 0; - qint64 medianTime = 0; + + void finalize() + { + static const qint64 qint64Max = std::numeric_limits::max(); + size_t size = durations.size(); + QTC_ASSERT(size <= qint64Max, size = qint64Max); + calls = static_cast(size); + + if (size == 0) + return; + + std::sort(durations.begin(), durations.end()); + const auto avg + = [](const qint64 a, const qint64 b) { return a / 2ll + b / 2ll + ((a & 1) + (b & 1)) / 2ll; }; + + const size_t half = size / 2; + median = (size & 1) == 0 ? avg(durations[half - 1], durations[half]) : durations[half]; + minimum = durations.front(); + maximum = durations.back(); + + durations.clear(); + } + + qint64 average() const + { + return calls == 0 ? 0 : total / calls; + } + + qint64 totalNonRecursive() const + { + return total - recursive; + } }; double durationPercent(int typeId) const; @@ -72,9 +107,10 @@ public: QStringList details(int typeIndex) const; QString summary(const QVector &typeIds) const; - const QHash &getData() const; + const QVector &getData() const; const QVector &getTypes() const; const QHash &getNotes() const; + qint64 rootDuration() const { return m_rootDuration; } int count() const; void clear(); @@ -93,7 +129,7 @@ private: void dataChanged(); void notesChanged(int typeIndex); - QHash m_data; + QVector m_data; QPointer m_calleesModel; QPointer m_callersModel; @@ -104,7 +140,8 @@ private: QStack m_callStack; QStack m_compileStack; - QHash> m_durations; + + qint64 m_rootDuration = 0; }; class QmlProfilerStatisticsRelativesModel : public QObject @@ -113,13 +150,14 @@ class QmlProfilerStatisticsRelativesModel : public QObject public: struct QmlStatisticsRelativesData { - QmlStatisticsRelativesData(qint64 duration = 0, qint64 calls = 0, bool isRecursive = false) - : duration(duration), calls(calls), isRecursive(isRecursive) {} + QmlStatisticsRelativesData(qint64 duration = 0, qint64 calls = 0, int typeIndex = -1, + bool isRecursive = false) + : duration(duration), calls(calls), typeIndex(typeIndex), isRecursive(isRecursive) {} qint64 duration; qint64 calls; + int typeIndex; bool isRecursive; }; - typedef QHash QmlStatisticsRelativesMap; QmlProfilerStatisticsRelativesModel(QmlProfilerModelManager *modelManager, QmlProfilerStatisticsModel *statisticsModel, @@ -128,7 +166,7 @@ public: int count() const; void clear(); - const QmlStatisticsRelativesMap &getData(int typeId) const; + const QVector &getData(int typeId) const; const QVector &getTypes() const; void loadEvent(RangeType type, const QmlEvent &event, bool isRecursive); @@ -139,7 +177,7 @@ signals: void dataAvailable(); protected: - QHash m_data; + QHash> m_data; QPointer m_modelManager; struct Frame { diff --git a/src/plugins/qmlprofiler/qmlprofilerstatisticsview.cpp b/src/plugins/qmlprofiler/qmlprofilerstatisticsview.cpp index 984c125c545..24cb91c58fe 100644 --- a/src/plugins/qmlprofiler/qmlprofilerstatisticsview.cpp +++ b/src/plugins/qmlprofiler/qmlprofilerstatisticsview.cpp @@ -418,13 +418,13 @@ void QmlProfilerStatisticsMainView::buildModel() void QmlProfilerStatisticsMainView::updateNotes(int typeIndex) { - const QHash &eventList = m_model->getData(); + const QVector &eventList = m_model->getData(); const QHash ¬eList = m_model->getNotes(); QStandardItem *rootItem = m_standardItemModel->invisibleRootItem(); for (int rowIndex = 0; rowIndex < rootItem->rowCount(); ++rowIndex) { int rowType = rootItem->child(rowIndex)->data(TypeIdRole).toInt(); - if (rowType != typeIndex && typeIndex != -1) + if (rowType == -1 || (rowType != typeIndex && typeIndex != -1)) continue; const QmlProfilerStatisticsModel::QmlEventStats &stats = eventList[rowType]; @@ -434,10 +434,10 @@ void QmlProfilerStatisticsMainView::updateNotes(int typeIndex) if (it != noteList.end()) { item->setBackground(colors()->noteBackground); item->setToolTip(it.value()); - } else if (stats.durationRecursive > 0) { + } else if (stats.recursive > 0) { item->setBackground(colors()->noteBackground); item->setToolTip(tr("+%1 in recursive calls") - .arg(Timeline::formatTime(stats.durationRecursive))); + .arg(Timeline::formatTime(stats.recursive))); } else if (!item->toolTip().isEmpty()){ item->setBackground(colors()->defaultBackground); item->setToolTip(QString()); @@ -468,14 +468,21 @@ QStringList QmlProfilerStatisticsMainView::details(int typeId) const void QmlProfilerStatisticsMainView::parseModel() { - const QHash &eventList = m_model->getData(); + const QVector &eventList = m_model->getData(); const QVector &typeList = m_model->getTypes(); - QHash::ConstIterator it; - for (it = eventList.constBegin(); it != eventList.constEnd(); ++it) { - int typeIndex = it.key(); - const QmlProfilerStatisticsModel::QmlEventStats &stats = it.value(); - const QmlEventType &type = (typeIndex != -1 ? typeList[typeIndex] : *rootEventType()); + QmlProfilerStatisticsModel::QmlEventStats rootEventStats; + rootEventStats.total = rootEventStats.maximum = rootEventStats.minimum = rootEventStats.median + = m_model->rootDuration(); + rootEventStats.calls = rootEventStats.total > 0 ? 1 : 0; + + for (int typeIndex = -1; typeIndex < eventList.size(); ++typeIndex) { + const QmlProfilerStatisticsModel::QmlEventStats &stats = typeIndex >= 0 + ? eventList[typeIndex] : rootEventStats; + if (stats.calls == 0) + continue; + + const QmlEventType &type = typeIndex >= 0 ? typeList[typeIndex] : *rootEventType(); QStandardItem *rootItem = m_standardItemModel->invisibleRootItem(); QList newRow; @@ -491,30 +498,30 @@ void QmlProfilerStatisticsMainView::parseModel() + QLatin1String(" %"), percent); newRow << new StatisticsViewItem( - Timeline::formatTime(stats.duration - stats.durationRecursive), - stats.duration - stats.durationRecursive); + Timeline::formatTime(stats.totalNonRecursive()), + stats.totalNonRecursive()); const double percentSelf = m_model->durationSelfPercent(typeIndex); newRow << new StatisticsViewItem(QString::number(percentSelf, 'f', 2) + QLatin1String(" %"), percentSelf); - newRow << new StatisticsViewItem(Timeline::formatTime(stats.durationSelf), - stats.durationSelf); + newRow << new StatisticsViewItem(Timeline::formatTime(stats.self), + stats.self); newRow << new StatisticsViewItem(QString::number(stats.calls), stats.calls); - const qint64 timePerCall = stats.calls > 0 ? stats.duration / stats.calls : 0; + const qint64 timePerCall = stats.average(); newRow << new StatisticsViewItem(Timeline::formatTime(timePerCall), timePerCall); - newRow << new StatisticsViewItem(Timeline::formatTime(stats.medianTime), - stats.medianTime); + newRow << new StatisticsViewItem(Timeline::formatTime(stats.median), + stats.median); - newRow << new StatisticsViewItem(Timeline::formatTime(stats.maxTime), - stats.maxTime); + newRow << new StatisticsViewItem(Timeline::formatTime(stats.maximum), + stats.maximum); - newRow << new StatisticsViewItem(Timeline::formatTime(stats.minTime), - stats.minTime); + newRow << new StatisticsViewItem(Timeline::formatTime(stats.minimum), + stats.minimum); newRow << new StatisticsViewItem(type.data().isEmpty() ? tr("Source code not available") : type.data(), type.data()); @@ -682,7 +689,7 @@ void QmlProfilerStatisticsRelativesView::displayType(int typeIndex) } void QmlProfilerStatisticsRelativesView::rebuildTree( - const QmlProfilerStatisticsRelativesModel::QmlStatisticsRelativesMap &map) + const QVector &data) { Q_ASSERT(treeModel()); treeModel()->clear(); @@ -690,11 +697,10 @@ void QmlProfilerStatisticsRelativesView::rebuildTree( QStandardItem *topLevelItem = treeModel()->invisibleRootItem(); const QVector &typeList = m_model->getTypes(); - QmlProfilerStatisticsRelativesModel::QmlStatisticsRelativesMap::const_iterator it; - for (it = map.constBegin(); it != map.constEnd(); ++it) { - const QmlProfilerStatisticsRelativesModel::QmlStatisticsRelativesData &stats = it.value(); - int typeIndex = it.key(); - const QmlEventType &type = (typeIndex != -1 ? typeList[typeIndex] : *rootEventType()); + for (auto it = data.constBegin(); it != data.constEnd(); ++it) { + const QmlProfilerStatisticsRelativesModel::QmlStatisticsRelativesData &stats = *it; + const QmlEventType &type = stats.typeIndex != -1 ? typeList[stats.typeIndex] + : *rootEventType(); QList newRow; // ToDo: here we were going to search for the data in the other model @@ -712,7 +718,7 @@ void QmlProfilerStatisticsRelativesView::rebuildTree( type.data(), type.data()); QStandardItem *first = newRow.at(RelativeLocation); - first->setData(typeIndex, TypeIdRole); + first->setData(stats.typeIndex, TypeIdRole); const QmlEventLocation location(type.location()); first->setData(location.filename(), FilenameRole); first->setData(location.line(), LineRole); diff --git a/src/plugins/qmlprofiler/qmlprofilerstatisticsview.h b/src/plugins/qmlprofiler/qmlprofilerstatisticsview.h index 351147d0e78..3123c84ec0c 100644 --- a/src/plugins/qmlprofiler/qmlprofilerstatisticsview.h +++ b/src/plugins/qmlprofiler/qmlprofilerstatisticsview.h @@ -166,7 +166,8 @@ signals: void gotoSourceLocation(const QString &fileName, int lineNumber, int columnNumber); private: - void rebuildTree(const QmlProfilerStatisticsRelativesModel::QmlStatisticsRelativesMap &map); + void rebuildTree( + const QVector &data); void updateHeader(); QStandardItemModel *treeModel();