Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bad Performance with large Series (tooltip specifically) #4093

Closed
onemenny opened this issue Apr 12, 2015 · 36 comments
Closed

Bad Performance with large Series (tooltip specifically) #4093

onemenny opened this issue Apr 12, 2015 · 36 comments
Assignees

Comments

@onemenny
Copy link

Hi,

We have been straggling with highcharts performance for long time now with no good results, and i would like to explore my options regarding.

assumptions:

  1. we use "evergreen browsers only" (latest chrome for that matter) & latest highcharts 4.1.4
  2. The basic situation is 5 charts, each with 2 series: arearange with line where each has ~3k-4k points, overall ~30k-40k points
  3. We use shared tooltip so we can show the data of all series (2 in this case)
  4. We synchronize crosshair between the charts using 'hchart.xAxis[0].translate'
  5. No animations whatsoever!
  6. Loading time is not an issue, but after the chart loads the user should be able to interact with it smoothly
  7. Using browser developer tools (chrome) the profiling shows long time on rendering, specifically tooltip refresh method
  8. Data grouping is not an options since its distorts the graph
  9. I have also run these tests in your performance lab which resulted in same low (very low) performance of the tooltip while hovering over the chart

I want to know if the following work around will have any affect:

  1. replace the highcharts tooltip with a third party tooltip like jqtip2 ?
  2. do some other tooltip megic (with your help) to increase the performance. ?
  3. Fall back to canvas instead of SVG. We noticed that flot.js (charts) have none of these problems and are very smooth? Can highcharts compete with that?

Thank you

@TorsteinHonsi
Copy link
Collaborator

There may be two reasons for the issue, first performance in the business code computing the tooltip, but more likely it's an issue with rendering the SVG fast enough on top of a complex SVG structure. That is a common browser problem.

  1. It will probably improve if you use a full HTML tooltip. See this demo.
  2. Yes, that would probably also do the trick. We have an experimental study with a stripped line series on canvas, but currently the tooltip doesn't work: http://jsfiddle.net/highcharts/8rcgrbm7/

@onemenny
Copy link
Author

@highslide-software we already use HTML tooltip, and by the way, even dropping the tooltip altogether and just having a crosshair is very slow. and the profiler shows its a rendering/draw latency. here is our options:
hideDelay: 0,
shared: true,
useHTML: true,
followPointer: false,
backgroundColor: null,
borderWidth: 0,
shadow: false,
animation: false,
...

the canvas experiment is just amazing, drawing 0.5m points in no time. why isn't there an option to fallback to canvas as first class citizen in highcharts options?
are there any plans to mitigate this soon? would be like a breath of fresh air for the slowness when datapoints.length > 2k

@TorsteinHonsi
Copy link
Collaborator

the canvas experiment is just amazing, drawing 0.5m points in no time. why isn't there an option to fallback to canvas as first class citizen in highcharts options?
are there any plans to mitigate this soon? would be like a breath of fresh air for the slowness when datapoints.length > 2k

We have been discussing this option, and we may also add it to the core. We imagine that it can drop in when the turboThreshold is exceeded. Challenges so far:

  • IE9-11 doesn't support foreightObject, so in those browsers we need to draw the canvas on top of the whole SVG, which in turn causes problems with tooltip inside the SVG. This is what we have done with the large heatmap demo, but with a HTML tooltip. We could potentially hack around it by writing the tooltip in a second svg on top of the canvas again.
  • The demo bypasses several parts of the code, like auto axis extremes detection, zooming etc. We will have to find a reasonable compromise between features and performance.

@onemenny
Copy link
Author

I can't even begin to express how important I think that is. Even if you don't support I.E. this is something our customers can leave with vs. slow charts that are not responsive at all.

Even if I consider taking your demo and working with it, it will take me quite some time to have it running with all other functionality (tooltip and other events will be enough). So this is not an option to me, it will be easier to customize flot charts instead IMHO.

Please find the compromise (it wont be that bad) as not having support for (not that large > 2k datapoints) large series.

I understand that working with a third party tooltip, from you experience will not get me any closer?
Is there anything I can disable regards the extra rendering SVG wise?

@TorsteinHonsi
Copy link
Collaborator

I've done some work on this approach now, and made great progress by copying the PNG data of the canvas directly into an SVG image tag. See the current implementation in this fiddle. Improvements:

  • Native tooltip now works.
  • IE is supported (except IE8 of course).
  • Export works.

@onemenny
Copy link
Author

Wow, this is amazing

I see that series show/hide also works. I am a bit confused about the responsiveness is/should be done in order that the chart will fit in the container (when resizing the window or the container), and of course the zoom in.

It seems to me this is something possible for a near future release. Other wise i can fall into many pitfalls overriding these series prototypes functions... can you think of any pitfalls in advance? none the less i think this should be an integral part of the product

@TorsteinHonsi
Copy link
Collaborator

I now added support for zooming and resizing, and probably most other cases of redrawing. See d55d84c.

It seems to me this is something possible for a near future release

There's still much work to be done, for example each series type needs its own optimized drawGraph function as well as perhaps drawPoints. And probably we should be able to define levels of optimizing, for example if you have 500,000 points, it is expensive to find the data min and max, but not so much with 10,000 points.

can you think of any pitfalls in advance?

Oh yes, plenty. We have bypassed extremes calculation, data cropping, data grouping in Highstock and even Point instanciation, and work on the original data array directly in order to do only one single loop over the data set. But I suggest we work on this incrementally if you find problems with it.

none the less i think this should be an integral part of the product

I agree to that. We could add optimize options so that the implementer can set when to go to canvas mode and how agressively we optimize. Something like this:

optimize: {
   threshold: 1000,
   autoExtremes: false,
   enableTooltip: true
}

@onemenny
Copy link
Author

Our use of highcharts in our app is very intense. We use it for dashboards, investigating large time series data graph and monitoring. So basically we use many kind of series types and enable our customers to change these types on the fly. So after reading your last comment I realize I will/may loose much functionality (series types, stacking options) since the nature of the solution is overriding the highcharts prototype.

Regarding the zoom, the drag gesture is nearly enough since we translate it to a server zoom. But just so you know, zooming to ~100 points in the chart the tooltip starts to get an offset for some points from their original position.

The other thing is that we use hchart.xAxis[0].translate to draw a crosshair synchronized between charts. I'm not sure that would work also

optimize: {
threshold: 1000,
autoExtremes: false,
enableTooltip: true
} >

The optimize feature is exactly what I was looking for in the first place. I'm not sure enableTooltip should take part of it since this option already exists in the tooltip itself. The 'threshold' option on the other had is good , but then I ask my self why do I need the SVG option in the first place? And what am I loosing here? Probably we cannot answer this right now. 'autoExtremes' is also good if it will enable me to have better performance. I also think this should be part of the chart options (across all series).

@TorsteinHonsi
Copy link
Collaborator

Great. I've pushed another commit which fixes the tooltip issue when zoomed in. Also improved the performance with built-in extremes calculation, so we can skip axis min and max options now.

Axis.translate will work like before, so your syncing between charts should not be affected.

The 'threshold' option on the other had is good , but then I ask my self why do I need the SVG option in the first place?

I would prefer SVG paths in smaller series because it

  • Allows animation on series update, add point, point update etc.
  • Provides an SVG element that can be styled and modified from internal and external scripts and CSS.
  • Renders sharper on retina displays (this can probably be optimized with canvas too, but not perfectly).
  • Produces ever-sharp vector graphics in exported SVG and PDF.
  • Is well tested with existing Highcharts features.

Also, SVG performs just as well with less than a few thousand points. See https://cloud.highcharts.com/show/atijef. A reasonable threshold form optimizing would be between 1000 and 10,000 points.

To summarize, I think the feature is best implemented as a module (like drilldown and exporting etc) which is part of the official API, but not part of the highcharts.js file. Not many implementers actually add 10 000 points or more, but if they do, they should meet the optimized renderer. In order to force that, we would need to throw an error or warning when implementers try to add too many points without using the optimizer. The solution would be to either use the optimizer or to raise the threshold, the point being that we need to educate implementers on this feature. Then the module would include optimized rendering for some series types:

  • area. Should be implemented, but there's a potential problem with null points. Stacking needs to run the translate method, which will slow it down, but the drawing of the shapes themselves can probably be done on canvas.
  • arearange. Should be implemented.
  • areaspline. Not implemented. No need for a spline with dense data, use line instead.
  • areasplinerange. As above.
  • bar. See column.
  • boxplot. Not implemented.
  • bubble. Should be implemented.
  • column. Could be implemented, but recommend area for dense data. Stacking and grouping present problems.
  • columnrange. Could be implemented, but recommend arearange for dense data.
  • errorbar. Not implemented.
  • funnel. Not implemented.
  • gauge. Not implemented.
  • heatmap. Should be implemented. Already partly implemented in the demos.
  • line. Draw the line, but the markers can probably be skipped as they don't make sense for dense data.
  • pie. Not implemented.
  • polygon. Not implemented.
  • pyramid. Not implemented.
  • scatter. Should be implemented.
  • solidgauge. Not implemented.
  • spline. Not implemented. No need for a spline with dense data, use line instead.
  • treemap. Should be implemented.
  • waterfall. Not implemented.

What do you think?

@onemenny
Copy link
Author

I see your point regarding SVG, and I cannot argue the animation case, or having DOM manipulations abilities, nor the time investment already made. Exporting is an asynch operation with no tooltip or other interactions, so falling back to SVG is more than possible. On the other hand you get canvas client export options if you care less for 'ever-sharp' graphics…

so for users like myself, that have already disabled any chart animation the canvas option is the way to go, which leads me to another question: im not sure how your plugin system operates. Does it override or extends the existing functionality? Will it misbehave with other plugins? Will it be supported in all major and minor releases?
So from my point of view I would like to see it integrated into the main highcharts source.
One last point, the transfer from 100 point to >2k to 10k or more, for intense graph users should be very smooth, cause you don’t always know where you'll end. Even with a good design you cant always know your range of points and having highcharts throw an error in production is not a call I'de like to get :) IMHO its better to have a fallback or pay with latency than to have an error. For advance users maybe integrate highcharts with the application loggin infrastructure (but this is not the thread for that)

By the way, thank you for the SVG canvas comparison! But I remind you that the coast was interacting with the chart (tooltip) and not the loading time, which is something that end-users can coop with (with a little UX help)

regarding the series type: (if not mentioned I agree with your point of view)

  • area. mandatory. I've seen the null point issue on another thread and I do believe null should be supported not as zero.
  • arearange. Mandatory on my side
  • column. Nice to have, still you can have charts with horizontal scroll, if area will work it will be a great fallback
  • heatmap. Should be implemented.
  • line. Mandatory, agree regarding the markers
  • scatter. Should be implemented.
  • treemap. Should be implemented.

Ill check you commit during the weekend
Thank you

@TorsteinHonsi
Copy link
Collaborator

im not sure how your plugin system operates. Does it override or extends the existing functionality? Will it misbehave with other plugins? Will it be supported in all major and minor releases?
So from my point of view I would like to see it integrated into the main highcharts source.

My proposal was to create a Highcharts module, which is part of our documented API, tests, docs etc. A plugin is a more loose, often third party piece of code.

One last point, the transfer from 100 point to >2k to 10k or more, for intense graph users should be very smooth, cause you don’t always know where you'll end. Even with a good design you cant always know your range of points and having highcharts throw an error in production is not a call I'de like to get :) IMHO its better to have a fallback or pay with latency than to have an error. For advance users maybe integrate highcharts with the application loggin infrastructure (but this is not the thread for that)

That's the downside of throwing an error. You set up a dynamic service, and suddenly an end user throws in 11,000 points and it breaks. On the other hand, making this and integrated part of the highcharts.js file will add several bytes to the 99.9% of the users who don't need it. (Yes, we are very cheap when it comes to adding bytes). We discussed this internally and found that we will first develop the module, then we get an impression of how much it adds.

On the other hand, we already have thresholds like this. For example, point configurations given as objects don't work when you have more than 1000 points of data, and will throw an error if you try. So the new proposal is just an exact continuation of that. See http://api.highcharts.com/#plotOptions.series.turboThreshold.

By the way, thank you for the SVG canvas comparison! But I remind you that the coast was interacting with the chart (tooltip) and not the loading time, which is something that end-users can coop with (with a little UX help)

According so some of our users, the loading time is also a problem. Also, we want to look good on side-by-side tests compared to competitors.

regarding the series type: (if not mentioned I agree with your point of view)
Yes, I think we are aligned here.

@onemenny
Copy link
Author

Ok
You have solid points.
So when can I expect something?

@TorsteinHonsi
Copy link
Collaborator

We're making progress on https://github.com/highslide-software/highcharts.com/blob/b432c9475e419d3780f13dd3903ade437fbd3d69/js/modules/series-on-canvas.experimental.src.js.

Line and scatter are working nicely now. I'll have a look at arearange now.

@TorsteinHonsi
Copy link
Collaborator

First draft of an arearange with half a million points: http://jsfiddle.net/highcharts/a129eret/. What do you think?

@onemenny
Copy link
Author

This is amazing, since the progress seems very rapid we decided to wait for highcharts implementations before moving to other solutions…

Here are my comments:

  • I see you add "drawing" label (when resizing and loading). This is something unique for the canvas implementation? I don’t see the showLoading() method in your code
  • Exporting does not work (of course) neither jpeg or png. It's kind of minor but will be important eventually
  • Show/hide series causes a full redraw, understandable but unless an element resize occur, is there a way to overcome this?
  • Chart selection event works well (http://jsfiddle.net/onemenny/a129eret/2/)
  • Chart load event works well (http://jsfiddle.net/onemenny/a129eret/2/)
  • HTML tooltip with positioner and formatter seems to work well!

@TorsteinHonsi
Copy link
Collaborator

Thanks! Yes, we will finish this path now. We're already on a good way to a full implementation.

I see you add "drawing" label (when resizing and loading). This is something unique for the canvas implementation? I don’t see the showLoading() method in your code

I made the drawing async, so that it now draws 50,000 points, does a timeout, draws another 50,000 points etc. That way we avoid locking up the user thread, so the browser will be responsive while drawing. The showLoading call is here.

Exporting does not work (of course) neither jpeg or png. It's kind of minor but will be important eventually

Exporting actually worked before I made the async refactoring. That's because the canvas image is encoded inside the SVG, so the export server treats it as a bitmap. The only thing we need to fix is to wait for all chunks to be rendered.

Show/hide series causes a full redraw, understandable but unless an element resize occur, is there a way to overcome this?

Yes, we should find a way to avoid that. Also when zooming in, then zooming out to full view, we should avoid redrawing everything. A possible solution is to cache the whole canvas contents and restore when showing/zooming out.

@onemenny
Copy link
Author

I think we are on the right track,
I would add the async drawing to the options for more granularity.

One thing that is important to consider here is the fact that we have ~10 charts minimum on a page. with infinite scrolling we can have more. so resizing is an issue, though it is minor

please keep me up to date

@vickychijwani
Copy link

I think this would be very useful for us as well. We have a dynamically-updated StockChart with 200K-1M points, updating at ~3 points/sec. We also have dataGrouping enabled with groupPixelWidth = 20, but it's causing a lot of problems (like #1229) and more importantly, distorts the data, as @onemenny said. And even with dataGrouping, performance isn't very good (especially the tooltip).

I love how performant the canvas demo is, great work on that! Using canvas would solve a lot of issues for us. Is there a plan to get this working with HighStock and dynamic charts too?

@TorsteinHonsi
Copy link
Collaborator

Thanks @vickychijwani !

@onemenny
Copy link
Author

@highslide-software great progress, the highstock seems to crash when changing the dates from the slider. series updating looks great, the tooltip seems to do a good job sliding over the chart when mouse moves...

is there a roadmap for next version?

@vickychijwani
Copy link

The data in series update example gets distorted as soon as I move my mouse pointer over it. It seems to be caused by boostThreshold. What does boostThreshold do exactly?

@vickychijwani
Copy link

One drawback of canvas seems to be that redrawing frequently is much more expensive than with SVG, probably because we're redrawing an entire PNG every time? Starting with 4000 points and completely zoomed out, each call to addPoint takes ~170 ms with SVG and ~300 ms with canvas (coarse measurements taken with console.time). Also, if I start with 5000 points on a stock chart and add a point every 0.5 sec thereafter, in most cases my browser hangs.

@onemenny
Copy link
Author

onemenny commented Aug 4, 2015

What is the status of this issue? What is the roadmap?
This feature is highly important to us, and we would like to know highcharts plans regarding

@TorsteinHonsi
Copy link
Collaborator

TorsteinHonsi commented Aug 4, 2015 via email

@onemenny
Copy link
Author

onemenny commented Aug 4, 2015

Ok, i would appreciate a version number or a timeline if that comes along.
I see that the boostThreshold was added so i fill comfortable testing this with my large series . is there anything else i should be subscribed to in order to get the updates?

@onemenny
Copy link
Author

Since the addition of zones in 4.1 We are planing to refactor our code to use this feature. Will it be supported in the boost module?

@roymj88
Copy link

roymj88 commented Nov 12, 2015

@highslide-software : 👍 for having this included. Much needed feature. Any update on the progress?

@TorsteinHonsi
Copy link
Collaborator

TorsteinHonsi commented Nov 16, 2015 via email

@chrisgervang
Copy link

@highslide-software looks like much great progress was made towards a solution, but the issue remains open. Any update?

@TorsteinHonsi
Copy link
Collaborator

Yes, we're fixing bugs on the Boost module, so if you have specific issues we can have a look.

@onemenny
Copy link
Author

@chrisgervang just for the reference, please see this: #5223

@cvasseng
Copy link
Contributor

cvasseng commented Apr 3, 2017

The recent boost overhaul introduced in 5.0.8 should address several of the above concerns. You should see a significant performance boost (no pun intended) both with the rendering itself, and things like tooltips and so on.

@tushar-gandhi
Copy link

I have faced performance issue for tooltip for large heatmap after upgrading to the Highchart 5.0.11. So I have used the boost module and found that performance issue has gone but new issue has got created where the color does not plot correctly. I have mentioned the same in the https://forum.highcharts.com/highcharts-usage/canvas-solution-for-long-heatmap-chart-t38375/ post.
Any solution for the same.

@pawelfus
Copy link
Contributor

pawelfus commented Jul 20, 2017

Color does plot correctly, but it looks like tooltip is rendered for a wrong point. In your screenshot you can see in tooltip: 70.13 -> so it's correct color, but wrong point. Short gif:

v9agcquqaf

Edit: I see it's reported as #6981.

@tushar-gandhi
Copy link

Please check the following JS Fiddle which behaves correctly which has same code but only difference is boost module is not included. You can see clear difference in the color plotted on charts with respect to mentioned JS Fiddle in issue.
Withput Boost Module:- https://jsfiddle.net/tushar412/mvk4c6e1/1/.
Also mentioned JSFiddle in the ticket if you mouse over on the chart with blue line you can see the legands over is on different color. Legand color is correct but chart plotting color is wrong.

@cvasseng
Copy link
Contributor

This has been fixed (see #6981).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

9 participants