Hiro Hiro - 2 months ago 13
Node.js Question

Perfomance issue highchart graph & socket.io

For the past few days I've been making this app, which is a realtime chart.
It works pretty well but my issue is the performance, if I leave the page open for about a minute, ram usage goes up to almost a gig (!) before chrome runs the garbage collector (i assume).
I think it's because I redraw the chart every time I update the series (every 5 seconds I believe) but I don't really know how to fix it.
Here is my code:

$(document).ready(function() {
var socket = io.connect();

socket.emit('requestStockData');

Highcharts.setOptions({
global: {
useUTC: false
}
});

chart = new Highcharts.Chart({
chart: {
renderTo: 'container',
defaultSeriesType: 'spline',
events: {
load: function() {
var chart = this;
loadChart(chart);
}
}
},
title: {
text: ''
},
xAxis: {
type: 'datetime',
tickInterval: 300 * 1000
},
yAxis: {
title: {
text: 'Prix en €'
}
}
});

function update(chart) {
socket.on('_sbourse', function(rawArray) {
for (var i = 0; i < rawArray.length; i++) {
var entry = rawArray[i];
var id = entry.id;
var str = entry.timestamp.toString();
var time = str.slice(0, -3);
var array = [
[parseInt(time), entry.price]
];
var array_to_point = [parseInt(time), entry.price];
$.each(chart.series, function(i, v) {
serieId = chart.series[i].options.id;
if (serieId == id) {
chart.series[i].addPoint([parseInt(time), entry.price], false);
}
});
}
chart.redraw(true);
});
}

function loadChart(chart) {
var array = {};
socket.on('stockData', function(rawArray) {
for (var i = 0; i < rawArray.length; i++) {
var entry = rawArray[i];
var id = entry.id;
var str = entry.timestamp.toString();
var time = str.slice(0, -3);
var array = [
[parseInt(time), entry.price]
];
var array_to_point = [parseInt(time), entry.price];
if (i < 18) {
chart.addSeries({
name: entry.name,
id: entry.id,
data: array
});
} else {
$.each(chart.series, function(i, v) {
serieId = chart.series[i].options.id;
if (serieId == id) {
chart.series[i].addPoint([parseInt(time), entry.price], false);
}
});
}
}
chart.redraw(true);
});
update(chart);
}
});


So yeah, I'd like to know if one of you guys already ran into this issue and achieved to fix it
PS: English is not my first language so I apologize if there is any major mistakes!

Answer

Yeah, I actually ran into this exact same problem not long ago.

1. Update the charts with new data instead of redrawing

Instead of redrawing your chart, just push the data using addPoint. It looks like you're doing this already but try to remove the chart.redraw() call. You might have to use setExtremes() to adjust the x-scale to include your new timestamp.

2. Use a background worker (SharedWorker) for socket.io

I did a lot of profiling (using the timeline feature) in Chrome, and it turns out that socket.io is the bottleneck here. It is continuously blocking the UI thread, and the heap just keeps growing until the browser crashes.

The solution I came up with was to move the socket.io connection to a background thread, using a SharedWorker (not WebWorker as it becomes idle and doesn't respond to socket connections).

So when your application starts, spin up a worker, connect to your socket. Then whenever the worker receives a message, send it to your application using postMessage instead.

I saw a great performance boost with this approach.

In Chrome you can inspect the worker using chrome://inspect/#workers

Comments