From 3280e951fd359fa8af90dee7002f70868abf0c17 Mon Sep 17 00:00:00 2001 From: Victor Zarubkin Date: Tue, 27 Feb 2018 21:48:15 +0300 Subject: [PATCH] #31 [Gui] Implemented zoom for complexity chart mode; Implemented export to .csv for complexity mode --- profiler_gui/arbitrary_value_inspector.cpp | 333 +++++++++++++-------- profiler_gui/arbitrary_value_inspector.h | 16 +- profiler_gui/graphics_slider_area.cpp | 6 +- 3 files changed, 223 insertions(+), 132 deletions(-) diff --git a/profiler_gui/arbitrary_value_inspector.cpp b/profiler_gui/arbitrary_value_inspector.cpp index e870e5c..3332332 100644 --- a/profiler_gui/arbitrary_value_inspector.cpp +++ b/profiler_gui/arbitrary_value_inspector.cpp @@ -160,65 +160,23 @@ void getChartPoints(const ArbitraryValuesCollection& _collection, Points& _point { _minValue = 1e300; _maxValue = -1e300; - - const auto size = _collection.size(); _points.clear(); - _points.reserve(size); - if (size == 0) + const auto& values = _collection.values(); + if (values.empty()) return; - const auto& valuesByThread = _collection.valuesMap(); - - if (valuesByThread.size() == 1) + _points.reserve(values.size()); + for (auto value : values) { - const auto& values = valuesByThread.begin()->second; - for (auto value : values) - { - const qreal x = sceneX(value->begin()); - const qreal y = profiler_gui::value2real(*value); - _points.emplace_back(x, y); + const qreal x = sceneX(value->begin()); + const qreal y = profiler_gui::value2real(*value); + _points.emplace_back(x, y); - if (y > _maxValue) - _maxValue = y; - if (y < _minValue) - _minValue = y; - } - } - else - { - std::list threadIds; - for (const auto& it : valuesByThread) - threadIds.push_back(it.first); - - size_t i = 0; - while (!threadIds.empty()) - { - for (auto it = threadIds.begin(); it != threadIds.end();) - { - const auto& values = valuesByThread.at(*it); - if (i >= values.size()) - { - it = threadIds.erase(it); - continue; - } - - const qreal x = sceneX(values[i]->begin()); - const qreal y = profiler_gui::value2real(*values[i]); - _points.emplace_back(x, y); - - if (y > _maxValue) - _maxValue = y; - if (y < _minValue) - _minValue = y; - - ++it; - } - } - - std::sort(_points.begin(), _points.end(), [](const QPointF& lhs, const QPointF& rhs) -> bool { - return lhs.x() < rhs.x(); - }); + if (y > _maxValue) + _maxValue = y; + if (y < _minValue) + _minValue = y; } } @@ -239,7 +197,7 @@ ArbitraryValuesCollection::ArbitraryValuesCollection() ArbitraryValuesCollection::~ArbitraryValuesCollection() { - + interrupt(); } ChartType ArbitraryValuesCollection::chartType() const @@ -247,7 +205,7 @@ ChartType ArbitraryValuesCollection::chartType() const return m_chartType; } -const ArbitraryValuesMap& ArbitraryValuesCollection::valuesMap() const +const ArbitraryValues& ArbitraryValuesCollection::values() const { return m_values; } @@ -267,14 +225,6 @@ ArbitraryValuesCollection::JobStatus ArbitraryValuesCollection::status() const return static_cast(m_status.load(std::memory_order_acquire)); } -size_t ArbitraryValuesCollection::size() const -{ - size_t totalSize = 0; - for (const auto& it : m_values) - totalSize += it.second.size(); - return totalSize; -} - profiler::timestamp_t ArbitraryValuesCollection::minDuration() const { return m_minDuration; @@ -296,7 +246,7 @@ qreal ArbitraryValuesCollection::maxValue() const } void ArbitraryValuesCollection::collectValues(ChartType _chartType, profiler::thread_id_t _threadId, profiler::vin_t _valueId - , const char* _valueName, profiler::block_id_t _parentBlockId, bool _directParent) + , const char* _valueName, profiler::block_id_t _parentBlockId) { interrupt(); @@ -399,6 +349,14 @@ void ArbitraryValuesCollection::collectById(profiler::thread_id_t _threadId, pro return; } + if (threadsCount > 1) + { + using Val = const profiler::ArbitraryValue*; + std::sort(m_values.begin(), m_values.end(), [] (Val lhs, Val rhs) { + return lhs->begin() < rhs->begin(); + }); + } + if (doCalculatePoints && !calculatePointsInner) getChartPoints(*this, m_points, m_minValue, m_maxValue); } @@ -413,6 +371,17 @@ void ArbitraryValuesCollection::collectById(profiler::thread_id_t _threadId, pro } } + if (m_chartType == ChartType::Complexity) + { + for (auto& it : m_complexityMap) + { + if (m_bInterrupt.load(std::memory_order_acquire)) + return; + auto& durations = it.second; + std::sort(durations.begin(), durations.end()); + } + } + setStatus(Ready); } @@ -423,7 +392,6 @@ bool ArbitraryValuesCollection::collectByIdForThread(const profiler::BlocksTreeR { // All values - auto& valuesList = m_values[_threadRoot.thread_id]; for (auto i : _threadRoot.events) { if (m_bInterrupt.load(std::memory_order_acquire)) @@ -438,7 +406,7 @@ bool ArbitraryValuesCollection::collectByIdForThread(const profiler::BlocksTreeR if (value->value_id() != _valueId) continue; - valuesList.push_back(value); + m_values.push_back(value); if (_calculatePoints) addPoint(*value); } @@ -468,6 +436,14 @@ void ArbitraryValuesCollection::collectByName(profiler::thread_id_t _threadId, c return; } + if (threadsCount > 1) + { + using Val = const profiler::ArbitraryValue*; + std::sort(m_values.begin(), m_values.end(), [] (Val lhs, Val rhs) { + return lhs->begin() < rhs->begin(); + }); + } + if (doCalculatePoints && !calculatePointsInner) getChartPoints(*this, m_points, m_minValue, m_maxValue); } @@ -482,6 +458,17 @@ void ArbitraryValuesCollection::collectByName(profiler::thread_id_t _threadId, c } } + if (m_chartType == ChartType::Complexity) + { + for (auto& it : m_complexityMap) + { + if (m_bInterrupt.load(std::memory_order_acquire)) + return; + auto& durations = it.second; + std::sort(durations.begin(), durations.end()); + } + } + setStatus(Ready); } @@ -492,7 +479,6 @@ bool ArbitraryValuesCollection::collectByNameForThread(const profiler::BlocksTre { // All values - auto& valuesList = m_values[_threadRoot.thread_id]; for (auto i : _threadRoot.events) { if (m_bInterrupt.load(std::memory_order_acquire)) @@ -503,7 +489,7 @@ bool ArbitraryValuesCollection::collectByNameForThread(const profiler::BlocksTre if (desc.type() != profiler::BlockType::Value || _valueName != desc.name()) continue; - valuesList.push_back(block.value); + m_values.push_back(block.value); if (_calculatePoints) addPoint(*block.value); } @@ -521,8 +507,6 @@ bool ArbitraryValuesCollection::depthFirstSearch(const profiler::BlocksTreeRoot& if (_threadRoot.children.empty()) return true; - auto& valuesList = m_values[_threadRoot.thread_id]; - using StackEntry = std::pair; using Stack = std::vector; @@ -555,7 +539,7 @@ bool ArbitraryValuesCollection::depthFirstSearch(const profiler::BlocksTreeRoot& const auto value = block.value; if (_isSuitableValue(value->value_id(), desc.name())) { - valuesList.push_back(value); + m_values.push_back(value); if (_calculatePoints) { const auto val = addPoint(*value); @@ -687,7 +671,6 @@ void ArbitraryValuesChartItem::paintMouseIndicator(QPainter* _painter, qreal _to // Horizontal const bool visibleY = (_top < y && y < _bottom); y = estd::clamp(_top, y, _bottom); - //if (visibleY) { _height -= ChartBounds; @@ -758,9 +741,6 @@ void ArbitraryValuesChartItem::paintMouseIndicator(QPainter* _painter, qreal _to else if (x > (_width - textWidthHalf)) left = _width - textWidth; - //if (!visibleY) - // _painter->setPen(Qt::blue); - const QRectF rect(left, _bottom + 2, textWidth, _font_h); _painter->drawText(rect, Qt::AlignCenter, valueString); _painter->drawLine(QLineF(x, _top, x, _bottom)); @@ -929,7 +909,7 @@ void ArbitraryValuesChartItem::updateRegularImageAsync(QRectF _boundingRect, qre if (_autoAdjust) { - for (auto it = first; it != points.end() && it->x() < right; ++it) + for (auto it = first; it != points.end() && it->x() < right && !isReady(); ++it) { if (it->x() < _value) continue; @@ -1118,11 +1098,21 @@ void ArbitraryValuesChartItem::updateComplexityImageAsync(QRectF _boundingRect, { const auto rectHeight = _boundingRect.height(); const auto screenWidth = _boundingRect.width() * _current_scale; + const auto globalSceneLeft = _minimum; //const auto maxColumnHeight = _boundingRect.height(); - m_workerImageScale = 1; - m_workerImageOrigin = _minimum; - m_workerImage = new QImage(screenWidth + 0.5, rectHeight, QImage::Format_ARGB32); + if (_bindMode) + { + m_workerImageScale = _range / _width; + m_workerImageOrigin = _value - _width * 3; + m_workerImage = new QImage(screenWidth * 7 + 0.5, rectHeight, QImage::Format_ARGB32); + } + else + { + m_workerImageScale = 1; + m_workerImageOrigin = _minimum; + m_workerImage = new QImage(screenWidth + 0.5, rectHeight, QImage::Format_ARGB32); + } m_workerImage->fill(0); QPainter p(m_workerImage); @@ -1138,10 +1128,6 @@ void ArbitraryValuesChartItem::updateComplexityImageAsync(QRectF _boundingRect, return; } - using LeftBounds = std::vector; - qreal realScale = _current_scale, offset = 0; - - const auto right = std::min(_value + _width, _maximum); qreal minValue = 1e300, maxValue = -1e300; profiler::timestamp_t minDuration = profiler_gui::numeric_max(), maxDuration = 0; for (const auto& c : m_collections) @@ -1173,6 +1159,80 @@ void ArbitraryValuesChartItem::updateComplexityImageAsync(QRectF _boundingRect, return; } + using LeftBounds = std::vector; + qreal left = minValue, right = maxValue; + + LeftBounds leftBounds; + leftBounds.reserve(m_collections.size()); + + if (_bindMode) + { + const auto valueRange = maxValue - minValue; + + _minimum = m_workerImageOrigin; + _maximum = m_workerImageOrigin + _width * 7; + + left = minValue + valueRange * (_minimum - globalSceneLeft) / _range; + right = minValue + valueRange * (_maximum - globalSceneLeft) / _range; + + if (_autoAdjust) + { + minDuration = profiler_gui::numeric_max(); + maxDuration = 0; + } + + maxValue = minValue + valueRange * (_value + _width - globalSceneLeft) / _range; + minValue += valueRange * (_value - globalSceneLeft) / _range; + } + + for (const auto& c : m_collections) + { + if (isReady()) + return; + + const auto& complexityMap = c.ptr->complexityMap(); + + if (complexityMap.empty()) + { + leftBounds.emplace_back(complexityMap.end()); + continue; + } + + if (!_bindMode) + { + leftBounds.emplace_back(complexityMap.begin()); + continue; + } + + auto first = complexityMap.lower_bound(left); + + if (first != complexityMap.end()) + { + if (first != complexityMap.begin()) + --first; + } + else + { + auto last = complexityMap.rbegin(); + first = (++last).base(); + } + + leftBounds.emplace_back(first); + + if (_autoAdjust) + { + for (auto it = first; it != complexityMap.end() && it->first < maxValue && !isReady(); ++it) + { + const auto value = it->first; + if (value < minValue || it->second.empty()) + continue; + + minDuration = std::min(minDuration, it->second.front()); + maxDuration = std::max(maxDuration, it->second.back()); + } + } + } + m_workerMaxValue = maxValue; m_workerMinValue = minValue; m_workerMaxDuration = maxDuration; @@ -1204,9 +1264,10 @@ void ArbitraryValuesChartItem::updateComplexityImageAsync(QRectF _boundingRect, return y; }; - const auto xmiddle = screenWidth * 0.5; - singleValue = fabs(maxValue - minValue) < 2 * std::numeric_limits::epsilon(); - const qreal width = std::max(maxValue - minValue, 0.01); + const qreal imageWidth = m_workerImage->width(); + const auto xmiddle = imageWidth * 0.5; + singleValue = fabs(right - left) < 2 * std::numeric_limits::epsilon(); + const qreal width = std::max(right - left, 0.01); const auto getx = [=] (qreal x) { if (singleValue) @@ -1215,14 +1276,15 @@ void ArbitraryValuesChartItem::updateComplexityImageAsync(QRectF _boundingRect, } else { - x -= minValue; - x *= screenWidth / width; + x -= left; + x *= imageWidth / width; } return x; }; std::vector averages; + size_t i = 0; for (const auto& c : m_collections) { if (isReady()) @@ -1230,7 +1292,17 @@ void ArbitraryValuesChartItem::updateComplexityImageAsync(QRectF _boundingRect, const auto& complexityMap = c.ptr->complexityMap(); if (complexityMap.empty()) + { + ++i; continue; + } + + const auto first = leftBounds[i]; + if (first == complexityMap.end()) + { + ++i; + continue; + } if (c.selected) { @@ -1250,7 +1322,9 @@ void ArbitraryValuesChartItem::updateComplexityImageAsync(QRectF _boundingRect, if (drawApproximateLine) averages.reserve(complexityMap.size()); - auto it = complexityMap.begin(); + auto it = first; + while (it->first < left) + ++it; qreal x = getx(it->first); @@ -1285,6 +1359,9 @@ void ArbitraryValuesChartItem::updateComplexityImageAsync(QRectF _boundingRect, average /= it->second.size(); averages.emplace_back(x, gety(average)); } + + if (it->first > right) + break; } if (drawApproximateLine) @@ -1404,6 +1481,7 @@ void ArbitraryValuesChartItem::clear() m_boundaryTimer.stop(); m_collections.clear(); m_minValue = m_maxValue = 0; + m_minDuration = m_maxDuration = 0; } void ArbitraryValuesChartItem::update(Collections _collections) @@ -1561,10 +1639,10 @@ int GraphicsChart::filterWindowSize() const return m_chartItem->filterWindowSize(); } -bool GraphicsChart::canShowSlider() const -{ - return chartType() != ChartType::Complexity && !m_bBindMode; -} +//bool GraphicsChart::canShowSlider() const +//{ +// return chartType() != ChartType::Complexity && !m_bBindMode; +//} ////////////////////////////////////////////////////////////////////////// @@ -1688,7 +1766,7 @@ ArbitraryValuesWidget::ArbitraryValuesWidget(QWidget* _parent) m_splitter->setStretchFactor(0, 1); m_splitter->setStretchFactor(1, 1); - m_filterWindowPicker->setRange(3, 25); + m_filterWindowPicker->setRange(3, 50); m_filterWindowPicker->setSingleStep(1); m_filterWindowPicker->setValue(8); @@ -2036,15 +2114,6 @@ void ArbitraryValuesWidget::onExportToCsvClicked(bool) } } - const auto writeHeader = [this, &csv] - { - csv.write(" ; ;\n"); // blank line - if (m_chart->chartType() == ChartType::Regular) - csv.write("timestamp;value;\n"); - else - csv.write("value;duration;\n"); - }; - if (m_chart->chartType() == ChartType::Regular) { for (auto item : m_checkedItems) @@ -2052,30 +2121,56 @@ void ArbitraryValuesWidget::onExportToCsvClicked(bool) const auto header = QString(" ; ;\nname:;%1;\ntimestamp;value;\n").arg(item->text(int_cast(ArbitraryColumns::Name))); csv.write(header.toStdString().c_str()); - auto collection = item->collection(); - auto it = collection->valuesMap().find(EASY_GLOBALS.selected_thread); - if (it != collection->valuesMap().end()) + const auto& values = item->collection()->values(); + for (auto value : values) { - for (auto val : it->second) - { - const auto str = QString("%1;%2;\n").arg(val->begin()).arg(profiler_gui::valueString(*val)); - csv.write(str.toStdString().c_str()); - } + const auto str = QString("%1;%2;\n").arg(value->begin()).arg(profiler_gui::valueString(*value)); + csv.write(str.toStdString().c_str()); } } } else { - QMessageBox::warning(this, "Warning", "Export to csv for complexity chart\nis not implemented yet", - QMessageBox::Close); + for (auto item : m_checkedItems) + { + const auto header = QString(" ; ; ; ; ;\nname:;%1; ; ; ;\nvalue;avg duration;min duration;max duration;count;\n") + .arg(item->text(int_cast(ArbitraryColumns::Name))); - // TODO: Implement complexity chart export to csv -// for (auto item : m_checkedItems) -// { -// const auto header = QString(" ; ;\nname:;%1;\nvalue;duration;\n").arg(item->text(int_cast(ArbitraryColumns::Name))); -// csv.write(header.toStdString().c_str()); -// } + csv.write(header.toStdString().c_str()); + + const auto& complexityMap = item->collection()->complexityMap(); + for (const auto& kv : complexityMap) + { + const auto& durations = kv.second; + + auto val = QString::number(kv.first, 'f', 12); + + const int last = val.size() - 1; + int c = last; + for (; c >= 0 && val[c] == QChar('0'); --c); + if (val[c] == QChar('.')) + --c; + + if (c != last) + val.chop(last - c); + + if (durations.empty()) + { + const auto str = QString("%1;0;0;0;0;\n").arg(val); + csv.write(str.toStdString().c_str()); + continue; + } + + const auto avg = std::accumulate(durations.begin(), durations.end(), profiler::timestamp_t(0)) / durations.size(); + const auto str = QString("%1;%2;%3;%4;%5;\n").arg(val).arg(avg).arg(durations.front()) + .arg(durations.back()).arg(durations.size()); + + csv.write(str.toStdString().c_str()); + } + } } + + csv.write("\n"); } void ArbitraryValuesWidget::buildTree(profiler::thread_id_t _threadId, profiler::block_index_t _blockIndex, profiler::block_id_t _blockId) diff --git a/profiler_gui/arbitrary_value_inspector.h b/profiler_gui/arbitrary_value_inspector.h index 6989595..658dc65 100644 --- a/profiler_gui/arbitrary_value_inspector.h +++ b/profiler_gui/arbitrary_value_inspector.h @@ -78,7 +78,6 @@ using Points = std::vector; using ArbitraryValues = std::vector; -using ArbitraryValuesMap = std::unordered_map >; using Durations = std::vector; using ComplexityValuesMap = std::map; @@ -112,7 +111,7 @@ private: using This = ArbitraryValuesCollection; - ArbitraryValuesMap m_values; + ArbitraryValues m_values; ComplexityValuesMap m_complexityMap; Points m_points; ThreadPoolTask m_worker; @@ -132,11 +131,10 @@ public: ~ArbitraryValuesCollection(); ChartType chartType() const; - const ArbitraryValuesMap& valuesMap() const; + const ArbitraryValues& values() const; const ComplexityValuesMap& complexityMap() const; const Points& points() const; JobStatus status() const; - size_t size() const; profiler::timestamp_t minDuration() const; profiler::timestamp_t maxDuration() const; @@ -144,7 +142,7 @@ public: qreal minValue() const; qreal maxValue() const; - void collectValues(ChartType _chartType, profiler::thread_id_t _threadId, profiler::vin_t _valueId, const char* _valueName, profiler::block_id_t _parentBlockId, bool _directParent); + void collectValues(ChartType _chartType, profiler::thread_id_t _threadId, profiler::vin_t _valueId, const char* _valueName, profiler::block_id_t _parentBlockId); bool calculatePoints(profiler::timestamp_t _beginTime); void collectValuesAndPoints(ChartType _chartType, profiler::thread_id_t _threadId, profiler::vin_t _valueId, const char* _valueName, profiler::timestamp_t _beginTime, profiler::block_id_t _parentBlockId); @@ -245,12 +243,6 @@ private: using This = GraphicsChart; ArbitraryValuesChartItem* m_chartItem; - qreal m_left; - qreal m_right; - qreal m_offset; - qreal m_xscale; - qreal m_visibleRegionWidth; - bool m_bBindMode; public: @@ -274,7 +266,7 @@ public: protected: - bool canShowSlider() const override; + //bool canShowSlider() const override; private slots: diff --git a/profiler_gui/graphics_slider_area.cpp b/profiler_gui/graphics_slider_area.cpp index a7ef4a9..322e55a 100644 --- a/profiler_gui/graphics_slider_area.cpp +++ b/profiler_gui/graphics_slider_area.cpp @@ -358,7 +358,11 @@ void GraphicsSliderArea::setValue(qreal _value) const profiler_gui::BoolFlagGuard guard(m_bUpdatingPos, true); - m_value = estd::clamp(m_minimumValue, _value, std::max(m_minimumValue, m_maximumValue - m_slider->width())); + const auto newValue = estd::clamp(m_minimumValue, _value, std::max(m_minimumValue, m_maximumValue - m_slider->width())); + if (fabs(m_value - newValue) < 2 * std::numeric_limits::epsilon()) + return; + + m_value = newValue; m_slider->setX(m_value + m_slider->halfwidth()); if (m_bEmitChange)