Высокая производительность при обновлении многих диаграмм

#javascript #python #plotly

#javascript #python #высокая производительность

Вопрос:

Я пытаюсь создать панель мониторинга в реальном времени, используя Python / Flask и Plotly JS. Я настроил веб-сокет (SocketIO) для перекачки данных в plotly и обновляю данные графика на основе этого. Чего я хотел бы добиться, так это построения графиков в реальном времени с частотой> 5 Гц (можно сортировать точки по пакетам каждые 200 мс).

Проблема в том, что в настоящее время, похоже, полностью отключает мой браузер, когда я делаю это с более чем ~ 5 графиками. Пожалуйста, посмотрите ниже, чего я пытаюсь достичь.

Панель управления построением

Я настроил его в JS следующим образом: (9x)

 <script>
                    Plotly.plot('X_AXIS',
                                [{x:[0],
                                  y:[0],
                                  type:'scatter'}],
                                {
                                    width: document.getElementById("col").offsetWidth,
                                    height:0.33 * document.getElementById("DataContent").offsetHeight,
                                    plot_bgcolor:"white",
                                    paper_bgcolor:("#ecc060"),
                                    margin:{l:15,r:15,t:30,b:30},
                                    xaxis: {range:[0,100]},
                                    yaxis: {range:[-10,10]},
                                    title: {text:'X-Axis'}
                                }
                                );
                </script>
  

С функцией обновления данных с помощью updateTraces:

 socket.on('UpdateTelemetry',function(Data){
        Plotly.extendTraces('X_AXIS',{x:[[Data[0][0]]],y:[[Data[0][1]]]},[0],100);
        Plotly.extendTraces('Y_AXIS',{x:[[Data[1][0]]],y:[[Data[1][1]]]},[0],100);
        Plotly.extendTraces('Z_AXIS',{x:[[Data[2][0]]],y:[[Data[2][1]]]},[0],100);
        Plotly.extendTraces('X_AXIS2',{x:[[Data[0][0]]],y:[[Data[0][1]]]},[0],100);
        Plotly.extendTraces('Y_AXIS2',{x:[[Data[1][0]]],y:[[Data[1][1]]]},[0],100);
        Plotly.extendTraces('Z_AXIS2',{x:[[Data[2][0]]],y:[[Data[2][1]]]},[0],100);
        Plotly.extendTraces('X_AXIS3',{x:[[Data[0][0]]],y:[[Data[0][1]]]},[0],100);
        Plotly.extendTraces('Y_AXIS3',{x:[[Data[1][0]]],y:[[Data[1][1]]]},[0],100);
        Plotly.extendTraces('Z_AXIS3',{x:[[Data[2][0]]],y:[[Data[2][1]]]},[0],100);


    });
  

Я пытался перейти на scattergl, но, честно говоря, производительность кажется хуже, чем просто scatter. Похоже, что plotly просто забивает все процессорное время, которое браузер получает для JS-кода, что означает, что остальная часть страницы становится почти полностью невосприимчивой. (больше даже не могу открывать выпадающие меню)

Есть ли более эффективный способ сделать это?

Ответ №1:

Я пытаюсь сделать что-то очень похожее и заметил те же ограничения Plotly. Я думаю, причина в том, что когда вы это делаете extendTraces , вы перерисовываете каждый график последовательно, он не объединяется в один рендеринг (https://github.com/plotly/plotly.js/blob/master/src/plot_api/plot_api.js#L935-L939 ). Однако существует способ вызывать extendTraces только один раз для всех трасс одновременно, что кажется гораздо более производительным (см. https://plotly.com/javascript/streaming/#streaming-subplots и https://codepen.io/federicomassa/pen/bGqGNeK ). Существенная разница здесь заключается в том, что по сравнению с вашим кодом я вызываю только extendTraces один раз, что, как я полагаю, приводит к перерисовке за одну итерацию рендеринга.

Если вы уже знали об этом решении и думали, что оно неприменимо к вашему случаю, потому что у вас есть асинхронные данные, поступающие для каждого графика, решение, которое я выбрал, — выбрать частоту обновления (скажем, 25 Гц) и собрать все данные, поступающие между двумя итерациями, в хранилище данных, которое вы можетеболее поздний запрос во время каждой итерации обновления. Вы даже можете хранить данные в простой структуре, подобной объекту Javascript, например:

 {
  data: {
    measure1: [
      { 
        timestamp: ...,
        value: ...
      }, ...
    ],
    measure2: ...
  }
}
  

Теперь во время каждого обновления вы можете вызывать extendTraces только один раз со всеми новыми измерениями для каждого подзаголовка.