c3js / c3

:bar_chart: A D3-based reusable chart library
http://c3js.org
MIT License
9.35k stars 1.39k forks source link

Push data performantly #995

Open pehrlich opened 9 years ago

pehrlich commented 9 years ago

Ok, so in my use case, I've got a reasonable number of points (some 500 per chart line, three lines per chart), and about every 20 ms I'm pushing a new data point with chart.load. This load causes a redraw, which causes the event rects (500) to be rebuilt. It seems like it should be possible to only redraw new or changed rects, while leaving the rest untouched.

Here's a performance profile:

screenshot 2015-02-12 16 25 07

Most of the time is being taken up in the two methods within redraw: redrawEventRect and redrawCircle. I suspect a couple of optimizations may be possible:

Perhaps there could be some sort of global dirty tracking, or more simply, a set for updating passed from load - if updates are necessary at all.

I am stymied for a moment in my digging, as redrawEventRect seems like it must be called for tooltips.

Can you suggest a good course of action?

Cheers!

PS The animation duration=0 update in 0.4.9 was a boon. thanks.

neil-morrison44 commented 9 years ago

I'm having similar issues, have you tried chart.flow? it only adds the new data, but doesn't seem to update the event rects for some reason.

pehrlich commented 9 years ago

The issue I was having with flow was this: I could only append data to the end. Adding a new line, without changing the width of the chart, didn't appear possible. (Unless the API eluded me!)

Without that, I'm not if I could get an accurate measure of performance, as it would have to re position all columns.

On Tuesday, February 17, 2015, Neil Morrison notifications@github.com wrote:

I'm having similar issues, have you tried chart.flow? it only adds the new data, but doesn't seem to update the event rects for some reason.

— Reply to this email directly or view it on GitHub https://github.com/masayuki0812/c3/issues/995#issuecomment-74688157.

masayuki0812 commented 9 years ago

@pehrlich Thanks for looking into this. Yeah, redrawCircle can be optimised, but now I didn't do it yet because circles will be used by selection feature. However, there must be something we can do, so I'll work on later. For event rects, umm I don't have any idea, but I'll look into this later as well.

pehrlich commented 9 years ago

FWIW, I hacked together the relevant looking bits, and got something pretty much fast enough (forgive me, but it is coffeescript with javascript excerpts):

#        console.time('c3 flush')
        # just update the data and redraw the line.
        # this could be further optimized, it looks like that is redrawing three lines, when only needs one?

        # 0: get $$ reference.
        $$ = scope.chart.internal

        # 1: update data
        # copied from internal.
        targets = $$.convertDataToTargets($$.convertColumnsToData( [ liveData ] )) # liveData is what we're plotting

        `
          // Update/Add data
          $$.data.targets.forEach(function (d) {
              for (var i = 0; i < targets.length; i++) {
                  if (d.id === targets[i].id) {
                      d.values = targets[i].values;
                      targets.splice(i, 1);
                      break;
                  }
              }
          });
          $$.data.targets = $$.data.targets.concat(targets); // add remained
          // Set targets
          $$.updateTargets($$.data.targets);
        `

        # 2: redraw
        lineIndices = $$.getShapeIndices($$.isLineType)
        drawLine = $$.generateDrawLine(lineIndices, false)
        $$.redrawLine(drawLine);
#        console.timeEnd('c3 flush')