I’m developing an application in which lots of graphs need to be rendered on every page. Some of these graphs are quite complex, and the callbacks that output them take 50-500ms on my high-end local development machine, even more so on the production server which is a carefully sized VM.
Server-side caching the graphs (via Redis or diskcache, for instance) is an excellent option in my case, but there is still an unnecessary cost that comes from JSON-loading the serialized graph object (via plotly.io.from_json) just to have it serialized it again by the callback that produces it (I bet that the decorated callback uses plotly.io.to_json to do that, internally). The code below is an example of how this could be done (nevermind the session-uuid store, it’s just a generic placeholder for inputs of any sort):
import plotly.io
from dash import Dash, dcc, html
from dash.dependencies import Input, Output
from dash.exceptions import PreventUpdate
from redis import Redis
redis = Redis(host="redis", port=6379)
app = Dash(__name__)
app.layout = html.Div([
dcc.Store("session-uuid", storage_type="session"),
dcc.Graph("costly-graph")
])
@app.callback(
Output("costly-graph", "figure"),
Input("session-uuid", "data")
)
def update_graph(session_uuid: str):
if session_uuid is None:
raise PreventUpdate()
figure_key = f"some-key-depending-on-{session_uuid}-or-any-other-input-for-instance"
figure_bytes = redis.get(figure_key)
if figure_bytes:
figure = plotly.io.from_json(figure_bytes.decode())
else:
figure = produce_costly_figure(session_uuid)
redis.set(figure_key, plotly.io.to_json(figure).encode())
return figure
def produce_costly_figure(session_uuid):
"""Some function that takes some 500+ms to complete."""
pass
Therefore, I wonder if there’s a way (or any plans/quick hacks) for “bypassing” the serialization, so that I can return a JSON string (produced/retrieved in whatever way) as the content of the output graph object. Following the example above, something along the lines of (note the hypothetical raw_json argument to Input):
@app.callback(
Output("costly-graph", "figure", raw_json=True),
Input("session-uuid", "data")
)
def update_graph(session_uuid: str):
if session_uuid is None:
raise PreventUpdate()
figure_key = f"some-key-depending-on-{session_uuid}-or-any-other-input-for-instance"
figure_bytes = redis.get(figure_key)
if not figure_bytes:
figure = produce_costly_figure(session_uuid)
figure_bytes = plotly.io.to_json(figure).encode()
redis.set(figure_key, figure_bytes)
return figure_bytes
By the way: I know about orjson and it does improve performance by a small margin, but I’m really concerned with squeezing every drop of juice by preventing a useless round trip!
Thanks in advance!
1 post - 1 participant