rstudio / shiny

Easy interactive web applications with R
https://shiny.posit.co/
Other
5.35k stars 1.87k forks source link

Complex Shiny app with many tabItems loads slowly #1797

Open zichner opened 7 years ago

zichner commented 7 years ago

I have a Shiny app with many (100-150) sub pages (using Shiny dashboard). All sub pages have the identical structure and just show different data (there are about 10 data tables, a few plots, and a few other controls on each page). Starting that Shiny application from RStudio takes rather long (about 1 min).

I built a minimal example:

library(shiny)
library(shinydashboard)
library(ggplot2)
library(plotly)
library(DT)

# Specify number of pages
numPages <- 150
pageIds <- paste0("page", seq_len(numPages))

# Shiny module UI function (10 data tables + 5 plotly plots per page)
modTestUI <- function(id) {
  ns <- NS(id)
  tagList(
    h1(id),
    DT::dataTableOutput(ns("table1")),
    DT::dataTableOutput(ns("table2")),
    DT::dataTableOutput(ns("table3")),
    DT::dataTableOutput(ns("table4")),
    DT::dataTableOutput(ns("table5")),
    DT::dataTableOutput(ns("table6")),
    DT::dataTableOutput(ns("table7")),
    DT::dataTableOutput(ns("table8")),
    DT::dataTableOutput(ns("table9")),
    DT::dataTableOutput(ns("table10")),
    plotOutput(ns("plot1")),
    plotOutput(ns("plot2")),
    plotOutput(ns("plot3")),
    plotOutput(ns("plot4")),
    plotOutput(ns("plot5"))
  )
}

# Shiny module server function
modTest <- function(input, output, session) {
  testData <- reactive({
    mtcars
  })

  output$table1 <- DT::renderDataTable({ DT::datatable(testData()) })
  output$table2 <- DT::renderDataTable({ DT::datatable(testData()) })
  output$table3 <- DT::renderDataTable({ DT::datatable(testData()) })
  output$table4 <- DT::renderDataTable({ DT::datatable(testData()) })
  output$table5 <- DT::renderDataTable({ DT::datatable(testData()) })
  output$table6 <- DT::renderDataTable({ DT::datatable(testData()) })
  output$table7 <- DT::renderDataTable({ DT::datatable(testData()) })
  output$table8 <- DT::renderDataTable({ DT::datatable(testData()) })
  output$table9 <- DT::renderDataTable({ DT::datatable(testData()) })
  output$table10 <- DT::renderDataTable({ DT::datatable(testData()) })

  output$plot1 <- renderPlot({ ggplot(testData(), aes(x = cyl, y = mpg, group = cyl)) + geom_boxplot() })
  output$plot2 <- renderPlot({ ggplot(testData(), aes(x = cyl, y = mpg, group = cyl)) + geom_boxplot() })
  output$plot3 <- renderPlot({ ggplot(testData(), aes(x = cyl, y = mpg, group = cyl)) + geom_boxplot() })
  output$plot4 <- renderPlot({ ggplot(testData(), aes(x = cyl, y = mpg, group = cyl)) + geom_boxplot() })
  output$plot5 <- renderPlot({ ggplot(testData(), aes(x = cyl, y = mpg, group = cyl)) + geom_boxplot() })
}

# UI function
ui <- dashboardPage(
  dashboardHeader(title = "Test"),
  dashboardSidebar(
    sidebarMenu(
      menuItem(text = "Dummy", tabName = "dummy"),
      lapply(pageIds, function(y) menuItem(text = y, tabName = y))
    )
  ),
  dashboardBody(
    do.call(
      tabItems, 
      c(
        list(tabItem(tabName = "dummy")),
        lapply(pageIds, function(x) { tabItem(tabName = x, modTestUI(x)) })
      )
    )
  )
)

# Server function
server <- function(input, output) {
  lapply(
    pageIds, 
    function(x) { callModule(modTest, id = x) }
  )
}

shinyApp(ui = ui, server = server)

(This loads faster than the full app, but usually still takes more than 30 seconds).

Is there a way to speed this up? (FYI: During the useR conference I briefly discussed this with @wch)

While testing the minimal example, I also noticed that the Shiny app sometimes loads much faster (rather 10s instead of 30s). However, it is not clear to me what causes this (it seems a rather nondeterministic behavior; the code did not change at all). Here are two Rprofvis profiles, one from a slow and one from a fast startup: profiles.zip

Would be great if you could look into this.

Thank you very much!

vnijs commented 7 years ago

Wonder if this might be related to https://github.com/rstudio/shiny/issues/1237

fabiangehring commented 7 years ago

I am facing the exact same problem (many tabs, using shinydashboard in combination with callModule)

I was surprised that (for a big ui)

test <- as.character(ui) # where ui <- dashboardPage(...)

is very slow and takes several seconds. I don't know the internals but expect something similar to be called every time the HTML-DOM is created from the ui. Any ideas if this could be the problem and how it could be speed up?

zach-merritt commented 5 years ago

@zichner Did you ever reach a solution here? I'm facing a similar issue.

jcheng5 commented 5 years ago

@zach-merritt FWIW, @trestletech recently investigated performance on htmltools and couldn't find any current instances where rendering 10,000's of UI objects was anything but blazing fast. Have you narrowed your performance bottleneck to HTML rendering, or are you talking about a general slowness?

zach-merritt commented 5 years ago

@jcheng5 it looks like I isolated the problem to a large selectizeinput within a shinyBS modal (>10K records). I'm going to bring it server side. Sorry for the false alarm. profvis wasn't too helpful, but I figured it out the old fashioned way - deleting stuff and seeing what happens. Sorry for false alarm :)

Huge fan of your work, thanks for responding!

jcheng5 commented 5 years ago

Yeah, plenty of people have been bitten by this before. As far as we understand it, the reason profvis doesn't help is because the R code executes very quickly--it's actually the selectize.js JavaScript trying to ingest all those tags in the browser that's slow.

Probably what we should do is emit a warning if you try to use client-side selectInput with a ton of choices...

LuShuYangMing commented 2 years ago

@zichner Did you ever reach a solution here? I'm facing a similar issue.