Streamlit性能优化:缓存与状态管理实战

目录

📌 核心特性

📌 运行原理

(1)全脚本执行

(2)差异更新

📌 缓存机制

❓为什么使用缓存?

使用@st.cache_data的优化方案

缓存适用场景

使用st.session_state的优化方案

📌总结


Streamlit 是一个开源的 Python 库,专为快速构建数据科学和机器学习 Web 应用而设计。它无需前端开发经验,通过简单 API 即可创建交互式界面,适合原型开发和数据展示

Streamlit官方地址:Streamlit • A faster way to build and share data apps

📌 核心特性

  1. 极简代码:用纯 Python 实现界面交互
  2. 实时预览:保存代码后自动刷新页面
  3. 丰富组件:支持图表、表格、滑块、文件上传等
  4. 无缝集成:兼容 Pandas、Matplotlib、PyTorch 等主流库

安装Streamlit

pip3 install streamlit

先通过一个简单的Hello World案例来了解Streamlit

import streamlit as st# 显示标题
st.title("Hello World,I'm echola")# 显示文本
st.write("这是一个由Streamlit搭建的Web平台")

运行:

 streamlit run hello.py

结果:

 是不是很强悍,三行代码搞定一个Web应用

📌 运行原理

Streamlit 的运行逻辑围绕脚本的线性执行响应式更新展开,其核心设计是让开发者以极简的方式构建交互式应用。以下是关键逻辑分步解析:

1、启动Web服务器

  •  Streamlit 启动一个本地 Web 服务器,默认监听 8501 端口
  • 打开浏览器并导航到 http://localhost:8501,展示应用界面

2、解析和执行脚本:

  • Streamlit 解析 hello.py 文件,生成抽象语法树(AST)
  • 动态执行脚本中的代码,按照顺序执行每个 Streamlit 组件(如 st.title 和 st.write)

3、组件渲染

  • 每个 Streamlit 组件(如 st.title st.write)会被注册到当前页面的状态中
  • 页面会根据组件的顺序和内容进行渲染

4、实时更新:

  • 基于Websocket通信:浏览器与服务器保持长连接,脚本输出的文本、图表等实时推送至前端
  • 增量更新机制:Streamlit只能对比前后两次执行的输出差异,仅向浏览器发送差异部分,也就是只更新变化的部分(而非刷新整个页面),Streamlit 会自动重新运行政整个脚本(而非局部更新)并更新页面,确保了开发过程中的高效性和实时性

上述增量更新可能会有一点矛盾,简而言之就是,「全脚本执行 + 差异更新」的设计,让 Streamlit 在开发便捷性(无需手动管理更新)和运行效率(局部渲染)之间取得了完美平衡

(1)全脚本执行

⚠️:也要避免全局作用域的冗余计算(需用缓存优化)

下来使用一个简单的案例,来模拟Streamlit加载全脚本的耗时过程

import time
import streamlit as st# 全脚本执行部分:以下代码每次交互都会运行
st.title("TimeOut Example")  # ✅ 标题会重复渲染,但 Streamlit 会优化为"增量更新"# 局部增量执行:以下代码仅在按钮点击时触发
if st.button("Click me"):processing_bar = st.progress(0)  # 每次点击时新建进度条with st.spinner("Loading..."):for percent_complete in range(100):time.sleep(0.05)processing_bar.progress(percent_complete + 1)st.success("Loading completed!")

 当用户点击按钮时,触发 if 条件判断,显示加载提示框 "Loading..."。开始模拟耗时操作,通过循环和 time.sleep 模拟耗时。每次循环中,更新进度条的值,进度条从0%逐渐增加到100%

直至耗时完成5s后,隐藏加载提示框,显示成功消息框”Loading completed“

再次点击【Click me】, 重复上述效果图

可以从上述效果中看出,无论是页面首次加载、按钮点击,还是其他组件交互(如下拉框选择),Streamlit都会从头到尾重新执行整个脚本

虽然脚本会全量执行,但Streamlit内部通过智能的组件状态管理和缓存机制,只更页面中发生变化的部分(如按钮触发的进度条),而不是刷新整个页面

接下来会使用缓存机制进行优化 

(2)差异更新

 可以高效渲染(减少网络传输数据量和浏览器渲染开销)和无缝体验(用户输入状态,如:文本框焦点、滚动条位置,不会因为局部更新而丢失)

⚠️:也要关注复杂UI的组件键(Key)的稳定性

📌 缓存机制

❓为什么使用缓存?

🔴 问题:每次点击click按钮时,代码会从执行整个耗时操作(for循环+time.sleep),即使操作结果不变

🥀 缓存的作用:将耗时操作的结果缓存起来,后续重复调用时直接读取缓存,避免重复计算

解决重复计算问题:通过装饰器@st.cache_data(缓存数据)或@st.cache_resource(缓存资源如模型、数据库连接),避免脚本执行导致的重复计算

@st.cache_data
def heavy_computation():# 此函数仅在输入参数或代码变更时重新执行return result

使用@st.cache_data的优化方案

那优化一下上面提到的问题

import time
import streamlit as stst.title("Optimize Example")# 缓存耗时操作的结束(假设操作是无参数)
@st.cache_data
def expensive_operation():# 模拟耗时操作(例如:数据计算)result = []for _ in range(100):time.sleep(0.05)  # 假设这是实际的计算步骤result.append(_)  # 模拟中间结果return resultif st.button("Click me"):processing_bar = st.progress(0)  # 每次点击时新建进度条with st.spinner("Loading..."):# 获取数据(首次点击执行耗时操作,后续点击直接读缓存)data = expensive_operation()for percent_complete in range(len(data)):processing_bar.progress(percent_complete + 1)st.success("Loading completed!")

首次点击【Click me】,会出现

大概5s后,执行完成

 重复点击【Click me】 ,不会重复加载进度条,由于直接读取缓存结果,无需重复计算,数据已缓存,进度条会快速更新到100%

通过 @st.cache_data 装饰器缓存耗时操作的结果,避免每次点击按钮时都重新执行耗时操作

不是所有耗时操作都必须使用缓存

缓存适用场景

  • 需要缓存的场景
    • 耗时操作的结果是 静态的(例如读取文件、初始化模型、复杂计算)。
    • 操作结果 不依赖外部变量或用户输入
  • 不适用缓存场景
    • 操作结果 依赖动态参数(例如用户输入的变量),此时需通过函数参数触发缓存更新。
    • 操作需要 实时更新(例如每次点击都需重新计算)

如果耗时操作 依赖参数,可以通过函数参数控制缓存版本:

@st.cache_data
def expensive_operation(param1, param2):# 根据参数执行不同计算results = []for _ in range(100):time.sleep(0.05)results.append(param1 + param2 + _)return results# 在按钮点击时传入参数
data = expensive_operation(10, 20)  # 参数不同会生成不同缓存

可以看出:

  • 缓存机制:通过 @st.cache_data 缓存静态计算结果,减少重复执行。
  • 进度条优化:将耗时操作与进度条更新分离,首次加载缓存后,后续交互可快速完成

那上述代码就没有什么问题了吗?

⚠️接下来分析原代码存在的弊端:

  1. 进度条重复创建:每次点击按钮都会新建processing_bar,导致多次点击时进度条堆叠
  2. 无法阻止重复提交:在耗时操作执行期间,用户仍可多次点击按钮,导致逻辑混乱
  3. 状态丢失:进度完成后的状态(如success提示)无法持久化

使用st.session_state的优化方案

1、保存进度条实例

if "processing_bar" not in st.session_state:st.session_state.processing_bar = None  # 初始化进度条容器if st.button("Click me"):# 仅在第一次点击时创建进度条if not st.session_state.processing_bar:st.session_state.processing_bar = st.progress(0)# 后续操作复用已有进度条with st.spinner("Loading..."):data = expensive_operation()for i in range(len(data)):st.session_state.processing_bar.progress(i + 1)# 完成后清空引用st.session_state.processing_bar = Nonest.success("Done!")

2. 防止重复提交

if "is_processing" not in st.session_state:st.session_state.is_processing = False  # 状态锁if st.button("Click me") and not st.session_state.is_processing:st.session_state.is_processing = True  # 锁定try:# 执行耗时操作...finally:st.session_state.is_processing = False  # 释放

3. 持久化完成状态

if "load_complete" not in st.session_state:st.session_state.load_complete = Falseif st.button("Click me"):# 执行操作...st.session_state.load_complete = Trueif st.session_state.load_complete:st.success("数据已加载完成!")st.balloons()  # 显示动画效果

 完整优化代码

import time
import streamlit as stst.title("Optimized Example")# 初始化会话状态
if "processing_bar" not in st.session_state:st.session_state.processing_bar = None
if "is_processing" not in st.session_state:st.session_state.is_processing = False
if "load_complete" not in st.session_state:st.session_state.load_complete = False@st.cache_data
def expensive_operation():result = []for _ in range(100):time.sleep(0.05)result.append(_)return resultif st.button("Click me") and not st.session_state.is_processing:st.session_state.is_processing = Truetry:# 创建或复用进度条if not st.session_state.processing_bar:st.session_state.processing_bar = st.progress(0)with st.spinner("Loading..."):data = expensive_operation()for i in range(len(data)):st.session_state.processing_bar.progress(i + 1)st.session_state.load_complete = Truefinally:st.session_state.is_processing = Falsest.session_state.processing_bar = None  # 重置进度条if st.session_state.load_complete:st.success("操作成功!")st.balloons()

关键作用总结

会话状态项功能说明
processing_bar保持进度条对象引用,防止重复创建
is_processing实现类似互斥锁,防止重复提交
load_complete持久化完成状态,实现跨脚本执行记忆

通过 st.session_state 实现了:

  1. 状态持久化:在 Streamlit 的全脚本重执行机制中保持关键状态
  2. 资源管理:避免 DOM 元素重复创建
  3. 交互安全:防止用户误操作导致的逻辑冲突

 这种模式特别适合需要保持复杂交互状态的场景(如多步骤表单、长任务处理)

📌总结

通过 缓存机制 减少重复计算,结合 st.session_state 管理会话状态,Streamlit 可以高效处理复杂交互场景,同时保持代码简洁和用户体验流畅。这种优化策略尤其适合需要频繁交互、状态保持或耗时操作的 Web 应用开发 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/901155.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

十七、TCP编程

TCP 编程是网络通信的核心,其 API 围绕面向连接的特性设计,涵盖服务端和客户端的交互流程。以下是基于 ​C 语言的 TCP 编程核心 API 及使用流程的详细解析: 核心 API 概览 ​函数​角色​描述socket()通用创建套接字,指定协议族…

将外网下载的 Docker 镜像拷贝到内网运行

将外网下载的 Docker 镜像拷贝到内网运行,可以通过以下步骤实现: 一、在有外网访问权限的机器上操作 下载镜像 使用docker pull命令下载所需的镜像。例如,如果你需要下载一个名为nginx的镜像,可以运行以下命令:docke…

《深入理解生命周期与作用域:以C语言为例》

🚀个人主页:BabyZZの秘密日记 📖收入专栏:C语言 🌍文章目入 一、生命周期:变量的存在时间(一)生命周期的定义(二)C语言中的生命周期类型(三&#…

Hqst的超薄千兆变压器HM82409S在Unitree宇树Go2智能机器狗的应用

本期拆解带来的是宇树科技推出的Go2智能机器狗,这款机器狗采用狗身体形态,前端设有激光雷达,摄像头和照明灯。在腿部设有12个铝合金精密关节电机,并配有足端力传感器,通过关节运动模拟狗的运动,并可做出多种…

壹起航:15年深耕,引领中国工厂出海新征程

在全球化浪潮汹涌澎湃的当下,中国工厂正以前所未有的热情和决心,将目光投向广阔的海外市场。然而,出海之路并非一帆风顺,建立品牌、获取稳定询盘、降低营销成本等难题,如同横亘在企业面前的高山,阻碍着他们…

【差分隐私相关概念】基础合成定理和高级合成技术简单关系

差分隐私中的合成定理用于分析多个机制组合时的隐私损失。基础合成定理和高级合成技术分别在不同场景下提供了隐私预算增长的估计,其关系如下: 基础合成定理(线性增长) 机制组合:当k个满足(ε, δ)-DP的机制按顺序组…

【异常处理】Clion IDE中cmake时头文件找不到 头文件飘红

如图所示是我的clion项目目录 我自定义的data_structure.h和func_declaration.h在unit_test.c中无法检索到 cmakelists.txt配置文件如下所示: cmake_minimum_required(VERSION 3.30) project(noc C) #设置头文件的目录 include_directories(${CMAKE_SOURCE_DIR}/…

MOS的驱动电流怎么计算?

一、MOS 驱动电流的计算方法 MOS 管在开关时,驱动电路主要是给栅极充放电。栅极电流 不是用来维持电流,而是用来克服电容的充放电需求,尤其是总栅极电荷 Qg。 驱动电流估算公式如下: I_drive Qg f_sw(Qg&#xff…

GGML源码逐行调试(下)

目录 前言1. 简述2. 预分配计算图内存2.1 创建图内存分配器2.2 构建最坏情况的计算图2.3 预留计算图内存 3. 分词4. 模型推理与生成4.1 模型推理4.2 采样 结语下载链接参考 前言 学习 UP 主 比飞鸟贵重的多_HKL 的 GGML源码逐行调试 视频,记录下个人学习笔记&#x…

1.5-APP的架构\微信小程序的架构

1.5-APP的架构\微信小程序的架构 APP的三种开发架构: 原生态APP类型 APP-开发架构-原生态-IDEA 演示:remusic项目源码 NP管理器: http://normalplayer.top/ HttpCanary:https://github.com/mingww64/HttpCanary-SSL-Magisk 安全影…

用css画一条弧线

ui里有一条弧线,现在用css实现 关键代码 border-bottom-left-radius: 100% 7px 两个参数分别代表横向和纵向的深度border-bottom-right-radius: 100% 7px

MSCKF及可观性总结

可观性 参考链接 真实VIO系统不能观的维度是4(位置和yaw角),由于EKF的转移和观测Jacobian矩阵的线性化点不同、不可观方向噪声的存在,实际MSCKF不能观的维度变成了3,绕重力轴的旋转(yaw角)被错…

【Hotspot虚拟机创建对象的过程是什么样的?】

1. 类加载检查 触发条件:当遇到 new 指令时,JVM首先检查该指令的参数(类符号引用)是否已在常量池中。检查内容: 类是否已被加载、解析和初始化。若未加载,则触发类加载过程(加载 → 验证 → 准…

南墙WAF非标端口防护实战解析——指定端口安全策略深度剖析

本文系统解析非标端口DDoS攻击防护难点,重点阐述南墙WAF在指定端口防御中的技术突破。通过某金融机构真实攻防案例,结合Gartner最新防御架构模型,揭示如何构建基于智能流量建模的精准防护体系,为金融、政务等关键领域提供可落地的…

Context的全面解析:在不同技术应用中的通用作用与差异

Context的全面解析:在不同技术应用中的通用作用与差异 引言: 在软件开发中,“Context”这个概念被广泛使用。它不仅限于某个特定的技术或编程语言,实际上,Context 作为一种抽象的设计模式,贯穿在许多开发领…

寻找峰值 --- 二分查找

目录 一:题目 二:算法原理 三:代码实现 一:题目 题目链接:162. 寻找峰值 - 力扣(LeetCode) 二:算法原理 三:代码实现 class Solution { public:int findPeakElemen…

基础算法训练7

目录 库存管理II 翻转对 合并K个升序链表 存在重复元素II 字符串相乘 字符串解码 在每个树行中找最大值 数据流的中位数 被包围的区域 为高尔夫比赛砍树 库存管理II LCR 159. 库存管理 III - 力扣(LeetCode) 解法一:先进行排序&a…

从单机版到超级APP:MCP如何解锁AI的超能力

MCP:AI界的“万能充电宝”——让AI从此告别“语言不通”的尴尬! 开篇:AI咖啡馆的尴尬日常 想象一下这样的场景: 一位AI助手在咖啡馆里手忙脚乱——它想帮用户点杯咖啡,但需要先写代码调用天气API(“今天下…

Grafana将弃用AngularJS-我们该如何迁移

AngularJS 弃用时间线 AngularJS 支持已在 Grafana 9 中正式弃用。在 2024 年 5 月发布的 Grafana 11 中,所有 Grafana Cloud 和自托管安装默认关闭该功能。到 Grafana 12 版本时,将完全移除对 AngularJS 的支持,包括配置参数开关 angular_s…

Qt之opengl定点数据添加更多属性

将颜色数据加入到定点数据中去 shader中代码 api中的代码 #include "sunopengl.h"#include <QTime>sunOpengl::sunOpengl(QWidget *parent) { } unsigned int VBO,VAO; float vertices[]{0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f,0.5f, -0.5f, 0.0f, 0.0f, 1.0f…