Speed up drawing

Help
Big Muscle
2010-01-13
2013-05-02
  • Big Muscle
    Big Muscle
    2010-01-13

    Hello. We use wxMathPlot to draw graphs in our application. But we need to operate with large amount of data. It has over 4 000 000 points on X axis, so drawing takes some time. Is it possible to improve drawing time? Make some optimizations?

    Currently, I noticed that wxMathPlot draws whole graph altough only part of it is selected. So I made some changes in wxMathPlot to draw selected area only and it is much much faster when drawing selections. Is it possible to implement something like this in wxMathPlot? I would send a patch, but I still have version 0.1.1 (version 0.1.2 makes no difference in time drawing).

    Thank you!

     
  • cdron77
    cdron77
    2010-01-14

    Well, its almost all about the mpLayer::Plot function. Optimization is always possible, version 0.1.2 contains some performance improvements, but  if you use very large data sets, it's better that you implement a new class derived from mpLayer, overriding the Plot method. This enables you to directly access your data (data are stored differently by each application, and it's not possible for wxMathPlot to address all of them), reducing data calculation. And you can optimize the code according to your needs. I have implemented such a class for my commercial projects and it works quite fine with large data sets.

    mpLayer implementations included in wxMathPlot are intended to be useful for simple applications and to be a tutorial for how to work with the library, but if you need something more professional, there's no doubt thet you have to work with your own layer.

    Anyway, patches are always welcome and if you send it to me, I'll be glad to merge it to the code. Don't worry about the version, I usually make a manual merge of the code.

    CD-RON77

     
  • andrewd
    andrewd
    2010-01-14

    If you have 4M x-points then that would far exceed the display resolution. You should pre-process the data to scale accordingly. Maybe though you need all the points for zooming? Still you could handle that.

     
  • Big Muscle
    Big Muscle
    2010-01-14

    Thanks for your answers.

    Yes, it's almost impossible to display all x-points on my resolution, so I was thinking about optimization that would draw only as much x-points as is needed to display (and not more), but I was unable to do it. So I made only optimization for zooming when only visible x-points are drawn (wxMathPlot draws all x-points).

    Following code is what I changed in mpFXY::Plot + I had to modify Rewind function to support rewind to specified position. Line numbers won't probably match because I made also other changes.

    --- mathplot.cpp.original   Thu Jan 14 15:13:37 2010
    +++ mathplot.cpp    Thu Jan 14 15:10:43 2010
    @@ -484,7 +486,12 @@
            dc.SetPen( m_pen);
    
            double x, y;
    -       Rewind();
    +       
    +       // MODIFIED BY BIGMUSCLE
    +       Rewind(w.GetPosX());
    +       GetNextXY(x, y);
    +       Rewind(w.GetPosX());
    +       double maxX = w.GetPosX() + (double)w.GetScrX() / w.GetScaleX();
    
            wxCoord startPx = m_drawOutsideMargins ? 0 : w.GetMarginLeft();
            wxCoord endPx   = m_drawOutsideMargins ? w.GetScrX() : w.GetScrX() - w.GetMarginRight();
    @@ -523,7 +530,7 @@
            {
                wxCoord cx0=0,cy0=0;
                bool    first = TRUE;
    -           while (GetNextXY(x, y))
    +           while (x <= maxX && GetNextXY(x, y))    // MODIFIED BY BIGMUSCLE
                {
                    wxCoord cx = w.x2p(x); // (wxCoord) ((x - w.GetPosX()) * w.GetScaleX());
                    wxCoord cy = w.y2p(y); // (wxCoord) ((w.GetPosY() - y) * w.GetScaleY());
    @@ -531,8 +538,16 @@
                    {
                        first=FALSE;
                        cx0=cx;cy0=cy;
    +                   MoveToEx((HDC)dc.GetHDC(), cx0, cy0, NULL); // ADDED BY BIG MUSCLE
    +               }
    +               // DrawLine does some unnecessary operations which slow down drawing
    +               //dc.DrawLine(cx0, cy0, cx, cy);
    +
    +               // BIGMUSCLE: if drawn points are not different, we won't draw
    +               if((int)cx0 != (int)cx || (int)cy0 != (int)cy)
    +               {
    +                   LineTo((HDC)dc.GetHDC(), cx, cy);
                    }
    -               dc.DrawLine(cx0, cy0, cx, cy);
                    cx0=cx; cy0=cy;
                }
            }
    @@ -2011,9 +2236,9 @@
         m_type = mpLAYER_PLOT;
     }
    
    -void mpFXYVector::Rewind()
    +void mpFXYVector::Rewind(int pos)  // MODIFIED BY BIGMUSCLE
     {
    -    m_index = 0;
    +    m_index = pos;
     }
    
     bool mpFXYVector::GetNextXY(double & x, double & y)
    
     
  • orbitcowboy
    orbitcowboy
    2010-01-15

    Hello all,

    i also improved the speed of mathplot according to the rules from Scott Meyers (Effective C++).
    I used the tool cppcheck, that tool gives hints where the code can be optimized.
    Here the output of cppcheck:

    : (possible style) Pre-Incrementing variable 'li' is preferred to Post-Incrementing
    : (possible style) Pre-Incrementing variable 'layIt' is preferred to Post-Incrementing
    : (possible style) Pre-Incrementing variable 'li' is preferred to Post-Incrementing
    : (possible style) Pre-Incrementing variable 'li' is preferred to Post-Incrementing
    : (possible style) Pre-Incrementing variable 'li' is preferred to Post-Incrementing
    : (possible style) Pre-Incrementing variable 'it' is preferred to Post-Incrementing
    : (possible style) Pre-Incrementing variable 'li' is preferred to Post-Incrementing
    : (possible style) Pre-Incrementing variable 'li' is preferred to Post-Incrementing
    : (possible style) Pre-Incrementing variable 'li' is preferred to Post-Incrementing
    : (possible style) Pre-Incrementing variable 'it' is preferred to Post-Incrementing
    : (possible style) Pre-Incrementing variable 'it' is preferred to Post-Incrementing

    Here the patch:

    Index: mathplot/mathplot.cpp
    ===================================================================
    --- mathplot/mathplot.cpp   (Revision 69)
    +++ mathplot/mathplot.cpp   (Arbeitskopie)
    @@ -1330,7 +1330,7 @@
                 UpdateAll();
             } else {
                 wxLayerList::iterator li;
    -            for (li = m_layers.begin(); li != m_layers.end(); li++) {
    +            for (li = m_layers.begin(); li != m_layers.end(); ++li) {
                     if ((*li)->IsInfo() && (*li)->IsVisible()) {
                         mpInfoLayer* tmpLyr = (mpInfoLayer*) (*li);
                         tmpLyr->UpdateInfo(*this, event);
    @@ -1721,7 +1721,7 @@
         bool        refreshDisplay )
     {
         wxLayerList::iterator layIt;
    -    for (layIt = m_layers.begin(); layIt != m_layers.end(); layIt++) 
    +    for (layIt = m_layers.begin(); layIt != m_layers.end(); ++layIt) 
         {
            if (*layIt == layer)
        {
    @@ -1799,7 +1799,7 @@
         // Draw all the layers:
         //trgDc->SetDeviceOrigin( m_scrX>>1, m_scrY>>1);  // Origin at the center
         wxLayerList::iterator li;
    -    for (li = m_layers.begin(); li != m_layers.end(); li++)
    +    for (li = m_layers.begin(); li != m_layers.end(); ++li)
         {
            (*li)->Plot(*trgDc, *this);
         };
    @@ -1924,7 +1924,7 @@
     {
         bool first = TRUE;
    
    -    for (wxLayerList::iterator li = m_layers.begin(); li != m_layers.end(); li++)
    +    for (wxLayerList::iterator li = m_layers.begin(); li != m_layers.end(); ++li)
         {
             mpLayer* f = *li;
    
    @@ -2165,7 +2165,7 @@
     {
         //wxNode *node = m_layers.GetFirst();
         unsigned int layerNo = 0;
    -    for(wxLayerList::iterator li = m_layers.begin(); li != m_layers.end(); li++)//while(node)
    +    for(wxLayerList::iterator li = m_layers.begin(); li != m_layers.end(); ++li)//while(node)
            {
             if ((*li)->HasBBox()) layerNo++;
        // node = node->GetNext();
    @@ -2181,7 +2181,7 @@
    
     mpLayer* mpWindow::GetLayerByName( const wxString &name)
     {
    -    for (wxLayerList::iterator it=m_layers.begin();it!=m_layers.end();it++)
    +    for (wxLayerList::iterator it=m_layers.begin();it!=m_layers.end();++it)
             if (! (*it)->GetName().Cmp( name ) )
                 return *it;
         return NULL;    // Not found
    @@ -2225,7 +2225,7 @@
        }
         // Draw all the layers:
         wxLayerList::iterator li;
    -    for (li = m_layers.begin(); li != m_layers.end(); li++)
    +    for (li = m_layers.begin(); li != m_layers.end(); ++li)
            (*li)->Plot(screenDC, *this);
    
        if (imageSize != wxDefaultSize) {
    @@ -2250,7 +2250,7 @@
     mpInfoLayer* mpWindow::IsInsideInfoLayer(wxPoint& point)
     {
         wxLayerList::iterator li;
    -    for (li = m_layers.begin(); li != m_layers.end(); li++) {
    +    for (li = m_layers.begin(); li != m_layers.end(); ++li) {
     #ifdef MATHPLOT_DO_LOGGING
             wxLogMessage(_("mpWindow::IsInsideInfoLayer() examinining layer = %p"), (*li));
     #endif // MATHPLOT_DO_LOGGING
    @@ -2306,7 +2306,7 @@
         m_axColour = axesColour;
        // cycle between layers to set colours and properties to them
         wxLayerList::iterator li;
    -    for (li = m_layers.begin(); li != m_layers.end(); li++) {
    +    for (li = m_layers.begin(); li != m_layers.end(); ++li) {
            if ((*li)->GetLayerType() == mpLAYER_AXIS) {
                wxPen axisPen = (*li)->GetPen(); // Get the old pen to modify only colour, not style or width
                axisPen.SetColour(axesColour);
    @@ -2437,12 +2437,12 @@
    
             std::vector<double>::const_iterator  it;
    
    -        for (it=xs.begin();it!=xs.end();it++)
    +        for (it=xs.begin();it!=xs.end();++it)
             {
                 if (*it<m_minX) m_minX=*it;
                 if (*it>m_maxX) m_maxX=*it;
             }
    -        for (it=ys.begin();it!=ys.end();it++)
    +        for (it=ys.begin();it!=ys.end();++it)
             {
                 if (*it<m_minY) m_minY=*it;
                 if (*it>m_maxY) m_maxY=*it;
    

    Best regards

    Martin

     
  • andrewd
    andrewd
    2010-01-15

    If an auto-increment has no side-effects (as mostly above) you'd think a compiler would just choose the fastest form (pre vs post) for the target. But apparently not.

     
  • Big Muscle
    Big Muscle
    2010-01-18

    I am interested whether changing for ppost- to pre-increment really does any speed difference.

     
  • cdron77
    cdron77
    2010-01-18

    In general that optimization is useful for cycles iterating for a long number of indexes: I believe it's hard to have more than 20-30 layers on an mpWindow: I didn't tested it, but I will be surprised to see a significant improvement from that optimization. Anyway, if it helps, from release 0.1.2 using debug build on Linux automatically enables gprof profiling, so you can see which are bottle necks when drawing. As far as I've seen, the problem is always the Plot method, which should be optimized according to the single application.

    A mayor optimization that is possible is to limit re-drawing only to the damaged area, not to whole mpWindow, but this not a straightforward task, and I don't believe I'll implement it soon.

     
  • orbitcowboy
    orbitcowboy
    2010-03-08

    I run the current version of cppcheck (1.41) against the current trunk of wxMathPlot. The tool discovered two places where speed can easily improved:

    : (possible style) Use xs.empty() instead of xs.size() to guarantee fast code.
    : (possible style) Use points_xs.empty() instead of points_xs.size() to guarantee fast code.

    Please refer the attached patch, that fixes the issue.

    Best regards

    Martin

    Here is the patch:

    Index: mathplot.cpp
    ===================================================================
    --- mathplot.cpp    (Revision 72)
    +++ mathplot.cpp    (Arbeitskopie)
    @@ -2432,7 +2432,7 @@
    
         // Update internal variables for the bounding box.
    -    if (xs.size()>0)
    +    if (!xs.empty())
         {
             m_minX  = xs[0];
             m_maxX  = xs[0];
    @@ -2855,7 +2855,7 @@
             m_shape_xs = points_xs;
             m_shape_ys = points_ys;
    
    -        if ( closedShape && points_xs.size())
    +        if ( closedShape && (!points_xs.empty()) )
             {
                 m_shape_xs.push_back( points_xs[0] );
                 m_shape_ys.push_back( points_ys[0] );
    
     
  • David McMinn
    David McMinn
    2010-08-19

    I know this thread is a few months old, but my question is related to the performance and I've only just begun to look into using wxMathPlot.

    On MSW it seems that any time you move the mouse over the chart area that it redraws all the layers, due to the code to handle the mouse motion events when no button is pressed. Depending on the number of points, layers and movement it is possible to achieve 100% CPU usage. Is there any need to redraw everything just because the mouse is moved?

    That's what I've noticed with sample1, with version 0.1.2, built in release mode.

    Thanks.