Plotly adapter renders every figure twice on page load
What happens
init.js calls Plotly.newPlot with empty stub traces for every figure at
script top level, awaits those promises, binds events, and only then sends
the init POST /dashboard/update. The response is drawn with Plotly.react.
Measured on gecko (headless Chromium 151, Plotly 3.0.0, one line trace,
1M rows in memory, 7 repeats):
| stage |
ms |
newPlot with empty stub traces |
28.2 |
| flexviz JS between newPlot and fetch |
0.5 |
| server + transfer |
4.2 |
Plotly.react with 1000 points |
15.9 |
| render barrier |
6.5 |
A single newPlot with the same 1000 points costs 31.7 ms. So the stub
render adds about 10 ms of duplicate Plotly work, and the page cannot send
the request until the stub render is done. The cost is per page load, flat
in trace count, and independent of row count.
Fix
Send the init update as soon as the runtime is loaded, and let the first
Plotly.react do the initial draw. Plotly.react on a div that has no
_fullLayout calls newPlot internally (plotly.js plot_api.js,
function react), so render.js needs no change.
Startup order becomes:
- Fire
restoreDashboardFromSpec() (the init POST) immediately.
- When a figure's first
react resolves, bind that figure once: Plotly
event handlers (plotly_relayout, plotly_selected, ...), the
ResizeObserver, panel controls, setFigureMode for non-cartesian
figures, and the mode indicator.
- Any figure the init response does not touch gets one
newPlot with its
stub traces after the response, so the div exists and events bind.
Estimated window: about 45 ms instead of 58 ms in the benchmark harness.
Users see 10 to 13 ms less on page load.
Trade-offs
- The panel is blank until the first response arrives. Today an empty axes
frame shows at once. A CSS placeholder covers this if it matters.
- A cheaper variant keeps the stub but fires the fetch in parallel with it.
That saves only about 5 ms because the request finishes long before the
stub render does.
- The benchmark probe (
flexviz_probe.js) keys its clock start on the
first newPlot call. After this change the clock must key on the request.
Scope
flexviz/adapters/js/plotly/init.js only, plus browser tests for init,
relayout, selection, and multi-figure dashboards. Roughly one day.
Plotly adapter renders every figure twice on page load
What happens
init.jscallsPlotly.newPlotwith empty stub traces for every figure atscript top level, awaits those promises, binds events, and only then sends
the init
POST /dashboard/update. The response is drawn withPlotly.react.Measured on gecko (headless Chromium 151, Plotly 3.0.0, one line trace,
1M rows in memory, 7 repeats):
newPlotwith empty stub tracesPlotly.reactwith 1000 pointsA single
newPlotwith the same 1000 points costs 31.7 ms. So the stubrender adds about 10 ms of duplicate Plotly work, and the page cannot send
the request until the stub render is done. The cost is per page load, flat
in trace count, and independent of row count.
Fix
Send the init update as soon as the runtime is loaded, and let the first
Plotly.reactdo the initial draw.Plotly.reacton a div that has no_fullLayoutcallsnewPlotinternally (plotly.jsplot_api.js,function react), sorender.jsneeds no change.Startup order becomes:
restoreDashboardFromSpec()(the init POST) immediately.reactresolves, bind that figure once: Plotlyevent handlers (
plotly_relayout,plotly_selected, ...), theResizeObserver, panel controls,setFigureModefor non-cartesianfigures, and the mode indicator.
newPlotwith itsstub traces after the response, so the div exists and events bind.
Estimated window: about 45 ms instead of 58 ms in the benchmark harness.
Users see 10 to 13 ms less on page load.
Trade-offs
frame shows at once. A CSS placeholder covers this if it matters.
That saves only about 5 ms because the request finishes long before the
stub render does.
flexviz_probe.js) keys its clock start on thefirst
newPlotcall. After this change the clock must key on the request.Scope
flexviz/adapters/js/plotly/init.jsonly, plus browser tests for init,relayout, selection, and multi-figure dashboards. Roughly one day.