使用future实现Shiny并行渲染反而更慢的原因咨询
Shiny中future并行耗时反而更长的原因分析
我分别测试了带future和不带future的Shiny示例,移除了10秒等待时间。理论上并行渲染应比非并行方案更快,但实际结果相反:带future的并行渲染耗时比非并行多1秒,我对此感到困惑,计划在大型应用中使用该机制但尚未理解其原理,恳请帮忙解释原因。
测试结果对比
- 不带future的脚本reactlog:

- 带future的脚本reactlog:

测试代码
带future的脚本
library(shiny) library(DT) library(ggplot2) library(data.table) library(promises) library(future) plan(multisession) ui <- navbarPage( tabPanel( "Новостные тренды" , sidebarLayout( sidebarPanel( br() , actionButton( "run_trends" , label = "run" , style="color: #fff; background-color: #337ab7; border-color: #2e6da4" ) , br() ) , mainPanel( textOutput("trends_time") , br() , br() , plotOutput('trend_plotly') , br() , p("results") , br() , DTOutput('trend_tbl') , br() , br() ) ) ) ) server <- function(input, output, session) { dt_trend <- eventReactive( input$run_trends, { dat_func <- function() { start_time <- Sys.time() dt <- data.table(x = rnorm(100), y = rnorm(100)) trendy_tbl <- head(dt, 10) ggplo1 <- ggplot(dt) + geom_point(aes(x=x,y=y)) list( trendy_tbl , ggplo1 , paste0('time: ', round(Sys.time() - start_time), ' сек.') ) } # Returning future future({ dat_func() }) }) output$trend_tbl <- renderDT({dt_trend() %>% then(~.[[1]])}) output$trend_plotly <- renderPlot({dt_trend() %>% then(~.[[2]])}) output$trends_time <- renderText({dt_trend() %>% then(~.[[3]])}) } shinyApp(ui, server)
不带future且运行更快的脚本
ui <- navbarPage( tabPanel( "Новостные тренды" , sidebarLayout( sidebarPanel( br() , actionButton( "run_trends" , label = "run" , style="color: #fff; background-color: #337ab7; border-color: #2e6da4" ) , br() ) , mainPanel( textOutput("trends_time") , br() , br() , plotOutput('trend_plotly') , br() , p("results") , br() , DTOutput('trend_tbl') , br() , br() ) ) ) ) server <- function(input, output, session) { observeEvent( input$run_trends,{ start_time <- Sys.time() dt <- data.table(x = rnorm(100), y = rnorm(100)) output$trend_tbl <- renderDT({head(dt, 10)}) output$trend_plotly <- renderPlot({ggplot(dt) + geom_point(aes(x=x,y=y))}) output$trends_time <- renderText({paste0('time: ', round(Sys.time() - start_time), ' сек.')}) }) } shinyApp(ui, server)
原因解释
多会话启动与环境复制开销
使用plan(multisession)会启动新的R子进程,子进程需要初始化环境、加载依赖包(即使主进程已加载,子进程仍需复制或重新加载必要的对象和包),这部分开销对于当前的轻量任务(生成100行数据集、绘制简单散点图)来说,远大于并行计算能节省的时间。未真正实现并行任务拆分
带future的代码是把所有计算逻辑打包到同一个future中执行,并没有将独立任务(比如生成表格数据、绘制图表)拆分成多个并行的future。本质上只是把单块计算移到了子进程,反而增加了进程间数据传输的成本(计算结果从子进程传回主进程需要序列化/反序列化)。非future版本的执行效率更高
不带future的代码在主进程中一次性完成所有计算并直接绑定到输出,没有进程切换、通信的额外开销,对于轻量任务来说自然更快。future的优势仅在处理耗时较长的独立任务时才能体现——比如复杂数据清洗、机器学习模型训练,此时进程启动的固定开销会被并行节省的时间抵消。
优化建议
- 如果要测试future的并行优势,需将独立任务拆分成多个future并行执行,例如:
dt_future <- future({data.table(x = rnorm(100), y = rnorm(100))}) plot_future <- future({ggplot(dt_future) + geom_point(aes(x=x,y=y))}) - 在大型应用中,仅对耗时超过数秒的任务使用future,避免为轻量任务引入不必要的开销。
- 可根据操作系统选择更轻量的future计划,比如Linux/macOS下使用
plan(multicore)(无需完全启动新R进程,开销更低),Windows下可配合future::globals参数减少环境复制的内容。
内容的提问来源于stack exchange,提问作者akshay bholee
相关产品推荐
相关产品推荐

