MCP HubMCP Hub
스킬 목록으로 돌아가기

optimize-shiny-performance

pjt222
업데이트됨 2 days ago
7 조회
17
2
17
GitHub에서 보기
기타general

정보

이 Claude Skill은 profvis, 캐싱(bindCache/memoise), 비동기 작업, 디바운싱과 같은 도구를 활용하여 Shiny 앱의 성능을 분석하고 최적화하도록 개발자를 지원합니다. 반응 속도가 느리게 느껴지거나, 부하 상황에서 리소스 사용량이 높거나, 많은 동시 사용자를 수용할 수 있도록 프로덕션 배포를 준비해야 하는 앱에 적합합니다. 본 스킬은 병목 현상을 식별하고 성능 개선을 실행 가능한 지침으로 제공합니다.

빠른 설치

Claude Code

추천
기본
npx skills add pjt222/agent-almanac -a claude-code
플러그인 명령대체
/plugin add https://github.com/pjt222/agent-almanac
Git 클론대체
git clone https://github.com/pjt222/agent-almanac.git ~/.claude/skills/optimize-shiny-performance

Claude Code에서 이 명령을 복사하여 붙여넣어 스킬을 설치하세요

문서


name: optimize-shiny-performance description: > 使用 profvis、bindCache、memoise、async/promises、debounce/throttle 和 ExtendedTask 分析并优化 Shiny 应用性能。适用于应用在用户交互时感觉缓慢或 无响应、并发负载下服务器资源耗尽、特定操作形成瓶颈,或为具有大量并发用户 的生产部署准备应用。 license: MIT allowed-tools: Read Write Edit Bash Grep Glob metadata: author: Philipp Thoss version: "1.0" domain: shiny complexity: advanced language: R tags: shiny, performance, profiling, caching, async, promises, optimization locale: zh-CN source_locale: en source_commit: 6f65f316 translator: claude-opus-4-6 translation_date: 2026-03-16

优化 Shiny 性能

通过缓存、异步操作和响应式图优化对 Shiny 应用性能进行分析、诊断和优化。

适用场景

  • Shiny 应用在用户交互时感觉缓慢或无响应
  • 并发用户负载下服务器资源耗尽
  • 特定操作(数据加载、绘图、计算)形成瓶颈
  • 为有大量用户的生产部署准备应用

输入

  • 必需:Shiny 应用路径
  • 必需:性能问题描述(加载慢、交互卡顿、内存占用高)
  • 可选:预期并发用户数
  • 可选:可用服务器资源(RAM、CPU 核心数)
  • 可选:应用是否使用数据库或外部 API

步骤

第 1 步:分析应用性能

# Profile with profvis
profvis::profvis({
  shiny::runApp("path/to/app", display.mode = "normal")
})

# Or profile specific operations
profvis::profvis({
  result <- expensive_computation(data)
})

识别主要瓶颈:

  1. 数据加载:初始数据获取需要多长时间?
  2. 响应式重算:哪些响应式触发最频繁?
  3. 渲染:哪些输出渲染时间最长?
  4. 外部调用:数据库查询、API 请求、文件 I/O?

使用响应式日志进行响应式图分析:

# Enable reactive logging
options(shiny.reactlog = TRUE)
shiny::runApp("path/to/app")
# Press Ctrl+F3 in the browser to view the reactive graph

预期结果: 明确识别出 2-3 个最大瓶颈。

失败处理: 如果 profvis 未显示有用细节,用 profvis::profvis() 包裹特定代码段。如果 reactlog 信息过多,每次专注于一个交互。

第 2 步:优化响应式图

减少不必要的响应式失效:

# BAD: Recomputes on ANY input change
output$plot <- renderPlot({
  data <- load_data()  # Runs every time
  filtered <- data[data$category == input$category, ]
  plot(filtered)
})

# GOOD: Isolate data loading from filtering
raw_data <- reactive({
  load_data()
}) |> bindCache()  # Cache the expensive part

filtered_data <- reactive({
  raw_data()[raw_data()$category == input$category, ]
})

output$plot <- renderPlot({
  plot(filtered_data())
})

使用 isolate() 防止不必要的失效:

# Only recompute when the button is clicked, not on every input change
output$result <- renderText({
  input$compute  # Take dependency on button
  isolate({
    paste("N =", input$n, "Mean =", mean(rnorm(input$n)))
  })
})

对高频输入使用 debounce()throttle()

# Debounce text input — wait 500ms after user stops typing
search_text <- reactive(input$search) |> debounce(500)

# Throttle slider — update at most every 250ms
slider_value <- reactive(input$slider) |> throttle(250)

预期结果: 响应式图只触发必要的重算。

失败处理: 如果移除某个依赖项破坏了功能,使用 req() 添加显式守卫,而非依赖隐式响应式依赖。

第 3 步:实现缓存

对 Shiny 输出使用 bindCache

output$plot <- renderPlot({
  create_expensive_plot(filtered_data())
}) |> bindCache(input$category, input$date_range)

output$table <- renderDT({
  expensive_query(input$filters)
}) |> bindCache(input$filters)

bindCache 使用输入值作为缓存键。当相同输入再次出现时,立即返回缓存结果。

对函数使用 memoise

# Cache expensive function results
load_reference_data <- memoise::memoise(
  function(dataset_name) {
    readr::read_csv(paste0("data/", dataset_name, ".csv"))
  },
  cache = cachem::cache_disk("cache/", max_age = 3600)
)

应用级数据预计算

# In global.R or outside server function — computed once at app startup
reference_data <- readr::read_csv("data/reference.csv")
model <- readRDS("models/trained_model.rds")

server <- function(input, output, session) {
  # reference_data and model are available to all sessions
  # without reloading
}

预期结果: 重复操作使用缓存结果;响应时间显著下降。

失败处理: 如果缓存增长过大,设置 max_agemax_size 限制。如果缓存值过期,减少 max_age 或添加清除缓存按钮。如果 bindCache 导致错误,确保缓存键输入可序列化。

第 4 步:为长时操作添加异步

使用 ExtendedTask(Shiny >= 1.8.1)处理长时运行计算:

server <- function(input, output, session) {
  # Define the extended task
  analysis_task <- ExtendedTask$new(function(data, params) {
    promises::future_promise({
      # This runs in a background process
      run_heavy_analysis(data, params)
    })
  }) |> bind_task_button("run_analysis")

  # Trigger the task
  observeEvent(input$run_analysis, {
    analysis_task$invoke(dataset(), input$params)
  })

  # Use the result
  output$result <- renderTable({
    analysis_task$result()
  })
}

对于 Shiny < 1.8.1 的应用,直接使用 promises:

library(promises)
library(future)
plan(multisession, workers = 4)

server <- function(input, output, session) {
  result <- eventReactive(input$compute, {
    future_promise({
      Sys.sleep(5)  # Simulate long computation
      expensive_analysis(isolate(input$params))
    })
  })

  output$table <- renderTable({
    result()
  })
}

预期结果: 长时操作不阻塞 UI;计算运行期间其他用户可以继续交互。

失败处理: 如果 future_promise 报错,检查是否设置了 plan(multisession)。如果变量在 future 中不可用,显式传递它们——future 在单独的 R 进程中运行。

第 5 步:优化渲染

减少渲染开销:

# Use plotly for interactive plots instead of re-rendering
output$plot <- plotly::renderPlotly({
  plotly::plot_ly(filtered_data(), x = ~x, y = ~y, type = "scatter")
})

# Use server-side DT for large tables
output$table <- DT::renderDataTable({
  DT::datatable(large_data(), server = TRUE, options = list(
    pageLength = 25,
    processing = TRUE
  ))
})

# Conditional UI to avoid rendering hidden elements
output$details <- renderUI({
  req(input$show_details)
  expensive_details_ui()
})

预期结果: 渲染操作更快且不阻塞 UI。

失败处理: 如果 plotly 在大数据集上较慢,使用 toWebGL() 进行 WebGL 渲染,或在绘图前对数据降采样。

第 6 步:验证性能改进

# Before/after benchmarking
system.time({
  shiny::testServer(myModuleServer, args = list(...), {
    session$setInputs(category = "A")
    session$flushReact()
  })
})

# Load testing with shinyloadtest
shinyloadtest::record_session("http://localhost:3838")
shinyloadtest::shinycannon(
  "recording.log",
  "http://localhost:3838",
  workers = 10,
  loaded_duration_minutes = 5
)
shinyloadtest::shinyloadtest_report("recording.log")

预期结果: 响应时间和/或并发用户容量有可测量的改善。

失败处理: 如果性能未改善,重新分析以找到下一个瓶颈。性能优化是迭代过程——先修复最大瓶颈,再重新测量。

验证清单

  • 分析识别出具体瓶颈(而非猜测)
  • 响应式图没有不必要的失效链
  • 昂贵操作使用缓存(bindCache 或 memoise)
  • 长时运行计算使用异步(ExtendedTask 或 promises)
  • 高频输入使用 debounce/throttle
  • 大数据集使用服务端处理
  • 性能改善可测量(前后计时对比)

常见问题

  • 过早优化:先分析。瓶颈很少在你认为的地方。
  • 缓存失效 bug:如果用户看到过期数据,缓存键没有包含所有相关输入。将缺失的依赖项添加到 bindCache()
  • future 变量作用域future_promise 在单独进程中运行。全局变量、数据库连接和响应式值必须显式捕获。
  • 响应式意大利面:如果响应式图复杂到难以理解,应用需要架构重构(模块),而不仅仅是缓存。
  • 过度缓存:缓存所有内容会浪费内存。只缓存昂贵且有重复输入模式的操作。

相关技能

  • build-shiny-module — 可维护响应式代码的模块化架构
  • scaffold-shiny-app — 从一开始选择合适的应用框架
  • deploy-shiny-app — 以适当服务器资源部署优化后的应用
  • test-shiny-app — 性能回归测试

GitHub 저장소

pjt222/agent-almanac
경로: i18n/zh-CN/skills/optimize-shiny-performance
0
agentsagentskillsai-assisted-developmentclaude-codeskillsteams

연관 스킬

llamaguard

기타

LlamaGuard는 폭력 및 혐오 발언 등 6가지 안전 범주에서 LLM 입력과 출력을 조정하기 위한 Meta의 70-80억 파라미터 모델입니다. 94-95% 정확도를 제공하며 vLLM, Hugging Face 또는 Amazon SageMaker를 사용해 배포할 수 있습니다. 이 기술을 사용하여 AI 애플리케이션에 콘텐츠 필터링 및 안전 가드레일을 손쉽게 통합하세요.

스킬 보기

cost-optimization

기타

이 Claude Skill은 리소스 적정화, 태깅 전략, 지출 분석을 통해 개발자들이 클라우드 비용을 최적화할 수 있도록 지원합니다. AWS, Azure, GCP에서 클라우드 비용을 절감하고 비용 거버넌스를 구현하기 위한 프레임워크를 제공합니다. 인프라 비용을 분석하거나, 리소스를 적정화하거나, 예산 제약을 충족해야 할 때 사용하세요.

스킬 보기

quantizing-models-bitsandbytes

기타

이 스킬은 bitsandbytes를 사용하여 LLM을 8비트 또는 4비트 정밀도로 양자화하며, 최소한의 정확도 손실로 50-75%의 메모리 감소를 달성합니다. 제한된 GPU 메모리에서 더 큰 모델을 실행하거나 추론을 가속화하는 데 이상적이며, INT8, NF4, FP4와 같은 형식을 지원합니다. 이 스킬은 HuggingFace Transformers와 통합되어 QLoRA 학습 및 8비트 옵티마이저를 가능하게 합니다.

스킬 보기

dispatching-parallel-agents

기타

이 Claude Skill은 3개 이상의 독립적인 문제를 동시에 조사하고 해결하기 위해 다중 에이전트를 배치합니다. 공유 상태나 의존성 없이 해결 가능한 무관련 장애 시나리오에 맞게 설계되었습니다. 핵심 기능은 병렬 문제 해결로, 각 독립 문제 영역마다 하나의 에이전트를 할당하여 효율성을 극대화합니다.

스킬 보기