性能#
参考:Performance | Dash for Python Documentation | Plotly
本章包含了一些改进你的 Dash 应用程序性能的建议。
Dash 应用程序的主要性能限制可能是应用程序代码本身的回调。如果你能加快回调的速度,你的应用程序会感觉更轻快。
记忆化#
因为 Dash 的回调本质上是函数性的(它们不包含任何状态),所以很容易添加记忆缓存。内存保存函数被调用后的结果,并在使用相同参数调用函数时重用结果。
关于在 Dash 应用中使用记忆来提高性能的简单例子,请参阅高级回调章节中的“用记忆提高性能”一节。
Dash 应用程序经常跨多个进程或线程部署。在这些情况下,每个进程或线程包含自己的内存,它不跨实例共享内存。这意味着如果我们使用 lru_cache
,缓存的结果可能不会在会话之间共享。
相反,我们可以使用 Flask-Caching 库,它将结果保存在一个像 Redis 这样的共享内存数据库中,或者作为文件保存在你的文件系统中。Flask-Caching 还有其他一些不错的特性,比如基于时间的逾时(expiry)。如果你想每小时或每天更新你的数据(清除你的缓存),基于时间的逾时是有用的。
下面是一个使用 Redis 的 Flask-Caching 的例子:
import datetime
import os
from dash import dcc, html
from dash.dependencies import Input, Output
from flask_caching import Cache
from app import app
import dash
dash.register_page(__name__)
cache = Cache(app.server, config={
# try 'filesystem' if you don't want to setup redis
'CACHE_TYPE': 'redis',
'CACHE_REDIS_URL': os.environ.get('REDIS_URL', '')
})
app.config.suppress_callback_exceptions = True
timeout = 20
layout = html.Div([
html.Div(id='flask-cache-memoized-children'),
dcc.RadioItems(
id='flask-cache-memoized-dropdown',
options=[
{'label': 'Option {}'.format(i), 'value': 'Option {}'.format(i)}
for i in range(1, 4)
],
value='Option 1'
),
html.Div('Results are cached for {} seconds'.format(timeout))
])
@app.callback(
Output('flask-cache-memoized-children', 'children'),
Input('flask-cache-memoized-dropdown', 'value'))
@cache.memoize(timeout=timeout) # in seconds
def render(value):
return 'Selected "{}" at "{}"'.format(
value, datetime.datetime.now().strftime('%H:%M:%S')
)
下面是一个缓存数据集而不是回调的例子。它使用 FileSystem 缓存,将缓存的结果保存到文件系统。
如果有一个数据集用于更新多个回调,那么这种方法很有效。
import datetime as dt
from dash import dcc, html
import numpy as np
import pandas as pd
from dash.dependencies import Input, Output
from flask_caching import Cache
from app import app
import dash
dash.register_page(__name__)
cache = Cache(app.server, config={
'CACHE_TYPE': 'filesystem',
'CACHE_DIR': 'cache-directory'
})
TIMEOUT = 60
@cache.memoize(timeout=TIMEOUT)
def query_data():
# This could be an expensive data querying step
df = pd.DataFrame(
np.random.randint(0, 100, size=(100, 4)),
columns=list('ABCD')
)
now = dt.datetime.now()
df['time'] = [now - dt.timedelta(seconds=5*i) for i in range(100)]
return df.to_json(date_format='iso', orient='split')
def dataframe():
return pd.read_json(query_data(), orient='split')
layout = html.Div([
html.Div(f'数据在最近 {TIMEOUT} 秒内更新'),
dcc.Dropdown(
id='live-dropdown',
value='A',
options=[{'label': i, 'value': i} for i in dataframe().columns]
),
dcc.Graph(id='live-graph')
])
@app.callback(Output('live-graph', 'figure'),
Input('live-dropdown', 'value'))
def update_live_graph(value):
df = dataframe()
now = dt.datetime.now()
return {
'data': [{
'x': df['time'],
'y': df[value],
'line': {
'width': 1,
'color': '#0074D9',
'shape': 'spline'
}
}],
'layout': {
# display the current position of now
# this line will be between 0 and 60 seconds
# away from the last datapoint
'shapes': [{
'type': 'line',
'xref': 'x', 'x0': now, 'x1': now,
'yref': 'paper', 'y0': 0, 'y1': 1,
'line': {'color': 'darkgrey', 'width': 1}
}],
'annotations': [{
'showarrow': False,
'xref': 'x', 'x': now, 'xanchor': 'right',
'yref': 'paper', 'y': 0.95, 'yanchor': 'top',
'text': 'Current time ({}:{}:{})'.format(
now.hour, now.minute, now.second),
'bgcolor': 'rgba(255, 255, 255, 0.8)'
}],
# aesthetic options
'margin': {'l': 40, 'b': 40, 'r': 20, 't': 10},
'xaxis': {'showgrid': False, 'zeroline': False},
'yaxis': {'showgrid': False, 'zeroline': False}
}
}
Graphs#
Plotly.js 开箱即用非常快。
大多数图形都是用 SVG 呈现的。这提供了清晰的渲染、发布质量的图像导出和广泛的浏览器支持。不幸的是,对于大型数据集(比如那些超过 15k 点的数据集),SVG 呈现图形可能很慢。为了克服这个限制, plotly.js
提供了一些图表类型的 WebGL 替代方案。WebGL 使用 GPU 渲染图形。
高性能的 WebGL 替代品包括:
:目前,dash 在更新时使用 plotly.js
的 newPlot
回调重新绘制整个图形。通过在此逻辑中引入 restyle
回调,可以显著提高更新图表的性能。
客户端回调#
客户端回调以 JavaScript 在客户端执行代码,而不是以 Python 在服务器端执行代码。
更多关于客户端回调的信息,请参阅 客户端回调 一章。