0
0
mirror of https://github.com/yse/easy_profiler.git synced 2024-12-26 08:01:51 +08:00

#31 [Gui] Arbitrary values viewer: fixed performance problem, added icons for chart types

This commit is contained in:
Victor Zarubkin 2018-02-16 20:51:33 +03:00
parent 3608ec1a56
commit 87bc825980
6 changed files with 336 additions and 27 deletions

View File

@ -55,13 +55,16 @@
#include <QAction>
#include <QActionGroup>
#include <QColor>
#include <QComboBox>
#include <QGraphicsScene>
#include <QHeaderView>
#include <QLabel>
#include <QMouseEvent>
#include <QPainter>
#include <QPainterPath>
#include <QResizeEvent>
#include <QSettings>
#include <QSpinBox>
#include <QSplitter>
#include <QToolBar>
#include <QVariant>
@ -77,37 +80,77 @@
EASY_CONSTEXPR int ChartBound = 2; ///< Top and bottom bounds for chart
EASY_CONSTEXPR int ChartBounds = ChartBound << 1;
template <size_t window_size>
std::vector<QPointF> gauss(const std::vector<QPointF>& _points)
void gaussFilter(std::vector<QPointF>& _points, int _windowSize, const std::atomic_bool& _interrupt)
{
if (_points.size() < 3)
return _points;
if (_points.size() < 3 || _windowSize < 3 || _interrupt.load(std::memory_order_acquire))
return;
std::vector<QPointF> out;
out.reserve(_points.size());
for (size_t i = 0, size = _points.size(); i < size; ++i)
{
if (_interrupt.load(std::memory_order_acquire))
return;
const auto next = i + 1;
if (next < window_size)
qreal sum = 0;
if (static_cast<int>(next) < _windowSize)
{
qreal sum = 0;
for (size_t j = 0; j <= i; ++j)
sum += _points[j].y();
sum /= i + 1;
out.emplace_back(_points[i].x(), sum);
}
else
{
qreal sum = 0;
for (size_t j = next - window_size; j <= i; ++j)
for (size_t j = next - _windowSize; j <= i; ++j)
sum += _points[j].y();
sum /= window_size;
out.emplace_back(_points[i].x(), sum);
sum /= _windowSize;
}
out.emplace_back(_points[i].x(), sum);
}
return out;
_points = std::move(out);
}
void medianFilter(std::vector<QPointF>& _points, int _windowSize, const std::atomic_bool& _interrupt)
{
if (_points.size() < 3 || _windowSize < 3 || _interrupt.load(std::memory_order_acquire))
return;
const auto windowSizeHalf = _windowSize >> 1;
std::vector<QPointF> out;
out.reserve(_points.size());
std::vector<qreal> window(static_cast<size_t>(_windowSize));
for (size_t i = 0, size = _points.size(); i < size; ++i)
{
if (_interrupt.load(std::memory_order_acquire))
return;
const auto next = i + 1;
if (next < windowSizeHalf)
{
int k = 0;
for (int j = static_cast<int>(next) - windowSizeHalf; k < _windowSize; ++j, ++k)
window[k] = _points[abs(j)].y();
}
else
{
int k = 0;
for (size_t j = next - windowSizeHalf; k < _windowSize; ++j, ++k)
window[k] = _points[j].y();
}
std::sort(window.begin(), window.end());
out.emplace_back(_points[i].x(), window[windowSizeHalf]);
}
_points = std::move(out);
}
void getChartPoints(const ArbitraryValuesCollection& _collection, Points& _points, qreal& _minValue, qreal& _maxValue)
@ -599,7 +642,9 @@ ArbitraryValuesChartItem::ArbitraryValuesChartItem()
, m_workerMinDuration(0)
, m_maxDuration(0)
, m_minDuration(0)
, m_filterWindowSize(8)
, m_chartType(ChartType::Regular)
, m_filterType(FilterType::None)
{
}
@ -760,6 +805,8 @@ void ArbitraryValuesChartItem::onImageUpdated()
{
m_maxValue = m_workerMaxValue;
m_minValue = m_workerMinValue;
m_maxDuration = m_workerMaxDuration;
m_minDuration = m_workerMinDuration;
}
void ArbitraryValuesChartItem::drawGrid(QPainter& _painter, int _width, int _height) const
@ -1235,7 +1282,29 @@ void ArbitraryValuesChartItem::updateComplexityImageAsync(QRectF _boundingRect,
if (drawApproximateLine)
{
p.drawPolyline(averages.data(), static_cast<int>(averages.size()));
// drawPolyLine() with 2 pixel width is VERY slow! Do not use it!
//p.drawPolyline(averages.data(), static_cast<int>(averages.size()));
// Draw polyline
{
QPointF p1 = averages.front();
auto averages_it = averages.begin();
for (++averages_it; averages_it != averages.end(); ++averages_it)
{
if (isReady())
return;
const QPointF& p2 = *averages_it;
const auto dx = fabs(p2.x() - p1.x()), dy = fabs(p2.y() - p1.y());
if (dx > 1 || dy > 1)
p.drawLine(p1, p2);
else
continue;
p1 = p2;
}
}
auto color = profiler_gui::darken(c.color, 0.65f);
if (profiler_gui::alpha(color) < 0xc0)
@ -1265,13 +1334,31 @@ void ArbitraryValuesChartItem::updateComplexityImageAsync(QRectF _boundingRect,
pen.setWidth(2);
p.setPen(pen);
//averages = std::move(gauss<8>(averages));
switch (m_filterType)
{
case FilterType::Median:
medianFilter(averages, m_filterWindowSize, m_bReady);
break;
case FilterType::Gauss:
gaussFilter(averages, m_filterWindowSize, m_bReady);
break;
default:
break;
}
if (isReady())
return;
QPainterPath pp;
pp.moveTo(averages.front());
const size_t step = std::max(averages.size() / (size_t)40, (size_t)1);
for (size_t k = 3 * step; k < averages.size(); k += 4 * step)
{
if (isReady())
return;
pp.cubicTo(averages[k - 2 * step], averages[k - 1 * step], averages[k]);
}
@ -1309,12 +1396,44 @@ void ArbitraryValuesChartItem::update(const ArbitraryValuesCollection* _selected
void ArbitraryValuesChartItem::setChartType(ChartType _chartType)
{
if (m_chartType != _chartType)
if (m_chartType == _chartType)
return;
cancelImageUpdate();
m_chartType = _chartType;
updateImage();
}
void ArbitraryValuesChartItem::setFilterType(FilterType _filterType)
{
if (m_filterType == _filterType)
return;
if (m_chartType == ChartType::Regular)
{
cancelImageUpdate();
m_chartType = _chartType;
updateImage();
m_filterType = _filterType;
return;
}
cancelImageUpdate();
m_filterType = _filterType;
updateImage();
}
void ArbitraryValuesChartItem::setFilterWindowSize(int _size)
{
if (m_filterWindowSize == _size)
return;
if (m_chartType == ChartType::Regular || m_filterType == FilterType::None)
{
m_filterWindowSize = _size;
return;
}
cancelImageUpdate();
m_filterWindowSize = _size;
updateImage();
}
ChartType ArbitraryValuesChartItem::chartType() const
@ -1322,6 +1441,16 @@ ChartType ArbitraryValuesChartItem::chartType() const
return m_chartType;
}
FilterType ArbitraryValuesChartItem::filterType() const
{
return m_filterType;
}
int ArbitraryValuesChartItem::filterWindowSize() const
{
return m_filterWindowSize;
}
//////////////////////////////////////////////////////////////////////////
GraphicsChart::GraphicsChart(QWidget* _parent)
@ -1378,11 +1507,31 @@ void GraphicsChart::setChartType(ChartType _chartType)
m_slider->setVisible(_chartType != ChartType::Complexity && !m_bBindMode);
}
void GraphicsChart::setFilterType(FilterType _filterType)
{
m_chartItem->setFilterType(_filterType);
}
void GraphicsChart::setFilterWindowSize(int _size)
{
m_chartItem->setFilterWindowSize(_size);
}
ChartType GraphicsChart::chartType() const
{
return m_chartItem->chartType();
}
FilterType GraphicsChart::filterType() const
{
return m_chartItem->filterType();
}
int GraphicsChart::filterWindowSize() const
{
return m_chartItem->filterWindowSize();
}
//////////////////////////////////////////////////////////////////////////
enum class ArbitraryColumns : uint8_t
@ -1493,6 +1642,10 @@ ArbitraryValuesWidget::ArbitraryValuesWidget(QWidget* _parent)
, m_splitter(new QSplitter(Qt::Horizontal, this))
, m_treeWidget(new QTreeWidget(this))
, m_chart(new GraphicsChart(this))
, m_filterBoxLabel(new QLabel(tr(" Approx filter:"), this))
, m_filterComboBox(new QComboBox(this))
, m_filterWindowLabel(new QLabel(tr(" Window size:"), this))
, m_filterWindowPicker(new QSpinBox(this))
, m_boldItem(nullptr)
{
m_splitter->setHandleWidth(1);
@ -1502,6 +1655,15 @@ ArbitraryValuesWidget::ArbitraryValuesWidget(QWidget* _parent)
m_splitter->setStretchFactor(0, 1);
m_splitter->setStretchFactor(1, 1);
m_filterWindowPicker->setRange(3, 25);
m_filterWindowPicker->setSingleStep(1);
m_filterWindowPicker->setValue(8);
m_filterComboBox->addItem(tr("None"));
m_filterComboBox->addItem(tr("Gauss"));
m_filterComboBox->addItem(tr("Median"));
m_filterComboBox->setCurrentIndex(0);
auto tb = new QToolBar(this);
tb->setIconSize(applicationIconsSize());
@ -1512,16 +1674,24 @@ ArbitraryValuesWidget::ArbitraryValuesWidget(QWidget* _parent)
auto actionGroup = new QActionGroup(this);
actionGroup->setExclusive(true);
action = new QAction(tr("Regular chart"), actionGroup);
action->setCheckable(true);
action->setChecked(true);
connect(action, &QAction::triggered, this, &This::onRegularChartTypeChecked);
tb->addAction(action);
auto actionRegulatChart = new QAction(QIcon(imagePath("yx-chart")), tr("Regular chart"), actionGroup);
actionRegulatChart->setCheckable(true);
actionRegulatChart->setChecked(true);
tb->addAction(actionRegulatChart);
action = new QAction(tr("Complexity chart"), actionGroup);
action->setCheckable(true);
connect(action, &QAction::triggered, this, &This::onComplexityChartTypeChecked);
tb->addAction(action);
auto actionComplexityChart = new QAction(QIcon(imagePath("big-o-chart")), tr("Complexity chart"), actionGroup);
actionComplexityChart->setCheckable(true);
tb->addAction(actionComplexityChart);
auto filtersWidget = new QWidget(this);
auto filtersLay = new QHBoxLayout(filtersWidget);
filtersLay->setContentsMargins(0, 0, 0, 0);
filtersLay->addWidget(m_filterBoxLabel);
filtersLay->addWidget(m_filterComboBox);
filtersLay->addWidget(m_filterWindowLabel);
filtersLay->addWidget(m_filterWindowPicker);
tb->addWidget(filtersWidget);
auto layout = new QVBoxLayout(this);
layout->setContentsMargins(0, 0, 0, 0);
@ -1557,6 +1727,24 @@ ArbitraryValuesWidget::ArbitraryValuesWidget(QWidget* _parent)
loadSettings();
m_filterComboBox->setCurrentIndex(int_cast(m_chart->filterType()));
m_filterWindowPicker->setValue(m_chart->filterWindowSize());
m_filterBoxLabel->setVisible(m_chart->chartType() == ChartType::Complexity);
m_filterComboBox->setVisible(m_chart->chartType() == ChartType::Complexity);
m_filterWindowLabel->setVisible(m_chart->chartType() == ChartType::Complexity && m_chart->filterType() != FilterType::None);
m_filterWindowPicker->setVisible(m_chart->chartType() == ChartType::Complexity && m_chart->filterType() != FilterType::None);
if (m_chart->chartType() == ChartType::Complexity)
actionComplexityChart->setChecked(true);
else
actionRegulatChart->setChecked(true);
connect(actionRegulatChart, &QAction::triggered, this, &This::onRegularChartTypeChecked);
connect(actionComplexityChart, &QAction::triggered, this, &This::onComplexityChartTypeChecked);
connect(m_filterComboBox, Overload<int>::of(&QComboBox::currentIndexChanged), this, &This::onFilterComboBoxChanged);
connect(m_filterWindowPicker, Overload<int>::of(&QSpinBox::valueChanged), this, &This::onFilterWindowSizeChanged);
rebuild();
}
@ -1722,6 +1910,12 @@ void ArbitraryValuesWidget::onRegularChartTypeChecked(bool _checked)
return;
m_chart->setChartType(ChartType::Regular);
m_filterWindowPicker->hide();
m_filterWindowLabel->hide();
m_filterComboBox->hide();
m_filterBoxLabel->hide();
repaint();
}
@ -1731,9 +1925,44 @@ void ArbitraryValuesWidget::onComplexityChartTypeChecked(bool _checked)
return;
m_chart->setChartType(ChartType::Complexity);
m_filterBoxLabel->show();
m_filterComboBox->show();
m_filterWindowLabel->setVisible(m_filterComboBox->currentIndex() != 0);
m_filterWindowPicker->setVisible(m_filterComboBox->currentIndex() != 0);
repaint();
}
void ArbitraryValuesWidget::onFilterComboBoxChanged(int _index)
{
switch (_index)
{
case 1:
m_filterWindowLabel->show();
m_filterWindowPicker->show();
m_chart->setFilterType(FilterType::Gauss);
break;
case 2:
m_filterWindowLabel->show();
m_filterWindowPicker->show();
m_chart->setFilterType(FilterType::Median);
break;
default:
m_filterWindowLabel->hide();
m_filterWindowPicker->hide();
m_chart->setFilterType(FilterType::None);
break;
}
}
void ArbitraryValuesWidget::onFilterWindowSizeChanged(int _size)
{
m_chart->setFilterWindowSize(_size);
}
void ArbitraryValuesWidget::buildTree(profiler::thread_id_t _threadId, profiler::block_index_t _blockIndex, profiler::block_id_t _blockId)
{
m_treeWidget->clear();
@ -1920,6 +2149,18 @@ void ArbitraryValuesWidget::loadSettings()
if (!state.isEmpty())
m_splitter->restoreState(state);
auto value = settings.value("chart/filterWindow");
if (!value.isNull())
m_chart->setFilterWindowSize(value.toInt());
value = settings.value("chart/filter");
if (!value.isNull())
m_chart->setFilterType(static_cast<FilterType>(value.toInt()));
value = settings.value("chart/type");
if (!value.isNull())
m_chart->setChartType(static_cast<ChartType>(value.toInt()));
settings.endGroup();
}
@ -1929,5 +2170,8 @@ void ArbitraryValuesWidget::saveSettings()
settings.beginGroup("ArbitraryValuesWidget");
settings.setValue("hsplitter/geometry", m_splitter->saveGeometry());
settings.setValue("hsplitter/state", m_splitter->saveState());
settings.setValue("chart/type", static_cast<int>(m_chart->chartType()));
settings.setValue("chart/filter", static_cast<int>(m_chart->filterType()));
settings.setValue("chart/filterWindow", m_chart->filterWindowSize());
settings.endGroup();
}

View File

@ -88,6 +88,13 @@ enum class ChartType : uint8_t
Complexity ///< Complexity chart; X axis = value, Y axis = duration
};
enum class FilterType : uint8_t
{
None = 0,
Gauss,
Median,
};
enum class ComplexityType : uint8_t
{
Constant = 0, ///< O(1)
@ -195,7 +202,9 @@ class ArbitraryValuesChartItem : public GraphicsImageItem
profiler::timestamp_t m_workerMinDuration;
profiler::timestamp_t m_maxDuration;
profiler::timestamp_t m_minDuration;
int m_filterWindowSize;
ChartType m_chartType;
FilterType m_filterType;
public:
@ -216,7 +225,12 @@ public:
void update(Collections _collections);
void update(const ArbitraryValuesCollection* _selected);
void setChartType(ChartType _chartType);
void setFilterType(FilterType _filterType);
void setFilterWindowSize(int _size);
ChartType chartType() const;
FilterType filterType() const;
int filterWindowSize() const;
private:
@ -263,7 +277,12 @@ public:
void update(Collections _collections);
void update(const ArbitraryValuesCollection* _selected);
void setChartType(ChartType _chartType);
void setFilterType(FilterType _filterType);
void setFilterWindowSize(int _size);
ChartType chartType() const;
FilterType filterType() const;
int filterWindowSize() const;
private slots:
@ -318,6 +337,10 @@ class ArbitraryValuesWidget : public QWidget
class QSplitter* m_splitter;
QTreeWidget* m_treeWidget;
GraphicsChart* m_chart;
class QLabel* m_filterBoxLabel;
class QComboBox* m_filterComboBox;
class QLabel* m_filterWindowLabel;
class QSpinBox* m_filterWindowPicker;
ArbitraryTreeWidgetItem* m_boldItem;
public:
@ -342,6 +365,8 @@ private slots:
void onCollectionsTimeout();
void onRegularChartTypeChecked(bool _checked);
void onComplexityChartTypeChecked(bool _checked);
void onFilterComboBoxChanged(int _index);
void onFilterWindowSizeChanged(int _size);
private:

View File

@ -41,3 +41,4 @@ default/arrow-up-hover.svg - Icon made by Freepik from www.flaticon.com
default/arrow-up-disabled.svg - Icon made by Freepik from www.flaticon.com
default/arrow-left.svg - Icon made by Freepik from www.flaticon.com
default/arrow-right.svg - Icon made by Freepik from www.flaticon.com
default/yx.svg - Icon made by Freepik from www.flaticon.com

View File

@ -0,0 +1,21 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="300.000000pt" height="142.000000pt" viewBox="0 0 300.000000 142.000000"
preserveAspectRatio="xMidYMid meet">
<g transform="translate(0.000000,142.000000) scale(0.100000,-0.100000)" stroke="none">
<path fill="#484848" d="M652 1345 c-107 -47 -280 -191 -368 -309 -57 -76 -127 -220 -150
-311 -25 -96 -25 -279 0 -360 37 -123 110 -221 197 -266 297 -151 792 86 986
472 162 325 103 708 -121 774 -224 67 -491 -91 -630 -372 -40 -82 -42 -93 -10
-93 15 0 46 11 69 25 35 20 51 41 95 125 59 112 121 189 174 214 76 36 195 7
263 -65 18 -19 44 -61 59 -94 24 -52 29 -76 32 -177 7 -180 -35 -331 -133
-478 -223 -335 -638 -372 -789 -70 -47 94 -59 163 -54 290 11 252 141 470 368
620 90 59 113 90 68 90 -13 -1 -38 -7 -56 -15z"/>
<path fill="#f44336" d="M2850 839 c-14 -12 -52 -35 -85 -52 -52 -27 -71 -31 -140 -31 -69 -1
-96 5 -200 42 -66 24 -148 46 -183 49 -82 8 -173 -17 -252 -68 l-60 -39 0 -96
0 -96 31 27 c18 14 59 40 93 58 53 27 71 31 141 31 69 1 96 -5 200 -42 66 -24
148 -46 183 -49 82 -8 173 17 252 68 l60 39 0 90 c0 49 -3 90 -7 90 -5 -1 -19
-10 -33 -21z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 395.304 395.304" style="enable-background:new 0 0 395.304 395.304;" xml:space="preserve">
<g>
<polygon fill="#484848" points="392.152,341.5 392.152,326.5 65.652,326.5 65.652,0 50.652,0 50.652,326.5 21.152,326.5 21.152,341.5
50.652,341.5 50.652,371 65.652,371 65.652,341.5 "/>
<path fill="#f44336" d="M229.731,307.947c37.193,0,71.676-30.068,97.096-84.667c24.571-52.773,38.103-122.773,38.103-197.105h-15
c0,72.19-13.034,139.941-36.701,190.774c-22.818,49.008-52.472,75.998-83.498,75.998s-60.68-26.99-83.498-75.998
c-23.667-50.833-36.701-118.584-36.701-190.774h-15c0,74.332,13.532,144.332,38.103,197.105
C158.056,277.879,192.539,307.947,229.731,307.947z"/>
<polygon fill="#f44336" points="393.455,365.304 382.849,354.696 373.152,364.393 363.455,354.696 352.849,365.304 362.545,375 352.849,384.696
363.455,395.304 373.152,385.607 382.849,395.304 393.455,384.696 383.759,375 "/>
<polygon fill="#f44336" points="31.849,11.696 22.152,21.393 12.455,11.696 1.849,22.304 11.545,32 1.849,41.696 12.455,52.304 42.455,22.304 "/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -47,5 +47,7 @@
<file alias="arrow-down">images/default/arrow-down.svg</file>
<file alias="arrow-down-hover">images/default/arrow-down-hover.svg</file>
<file alias="arrow-down-disabled">images/default/arrow-down-disabled.svg</file>
<file alias="yx-chart">images/default/yx.svg</file>
<file alias="big-o-chart">images/default/big-o.svg</file>
</qresource>
</RCC>