How to stop a function in R that is taking too lon

2019-03-15 02:04发布

I'm trying to do a thing "the right way". Sometimes "the right way" takes too long, depending on the inputs. I can't really know a priori when this will be. When "the right way" is taking too long, I want to go to "the hackish way". How do I make R monitor how long a particular task as taken, and give it something else to do if a threshold has passed? I'd imagine that this will be part of the try family, but I'm not quite sure what to call it or google for.

Dummy example below. When slow.func takes too long, I want interuptor to stop it and call fast.func instead.

slow.func <- function(x){
    Sys.sleep(x)    
    print('good morning')
}

fast.func <- function(x){
    Sys.sleep(x/10) 
    print('hit snooze')
}

interuptor = function(FUN,args, time.limit, ALTFUN){
#   START MONITORING TIME HERE
    do.call(FUN,args)
#   IF FUN TAKES TOO LONG, STOP IT, CALL A
    do.call(ALTFUN,args)
}

interuptor(slow.func, list(x = 2), time.limit = 1, fast.func)

3条回答
一夜七次
2楼-- · 2019-03-15 02:37

The answer of "nwknoblauch" does not work for me unless I change "warning" by "silent" inside the interruptor function.

library(R.utils)

slow.func <- function(x){
  Sys.sleep(10)    
  return(x^2)
}

fast.func <- function(x){
  Sys.sleep(2) 
return(x*x)
}

interruptor = function(FUN,args, time.limit, ALTFUN){
  results <- NULL
  results <- evalWithTimeout({FUN(args)},timeout=time.limit,onTimeout="silent")
  if(is.null(results)){
    results <- ALTFUN(args)
  }
  return(results)
}   
interruptor(FUN = slow.func,args=2,time.limit=3,ALTFUN = fast.func)
查看更多
等我变得足够好
3楼-- · 2019-03-15 02:41

The R package R.utils has a function evalWithTimeout that's pretty much exactly what you're describing. If you don't want to install a package, evalWithTimeout relies on the less user friendly R base function setTimeLimit

Your code would look something like this:

library(R.utils)

slow.func <- function(x){
  Sys.sleep(10)    
  return(x^2)
}

fast.func <- function(x){
  Sys.sleep(2) 
return(x*x)
}
interruptor = function(FUN,args, time.limit, ALTFUN){
  results <- NULL
  results <- evalWithTimeout({FUN(args)},timeout=time.limit,onTimeout="warning")
  if(results==NULL){
    results <- ALTFUN(args)
  }
  return(results)
}   
interruptor(slow.func,args=2,time.limit=3,fast.func)
查看更多
神经病院院长
4楼-- · 2019-03-15 02:57

For anyone who wants a lighter weight solution that does not depend on the R.utils package, I ended up using a minimal solution based on the withTimeout() code.

foo <- function() {

  time_limit <- 10

  setTimeLimit(cpu = time_limit, elapsed = time_limit, transient = TRUE)
  on.exit({
    setTimeLimit(cpu = Inf, elapsed = Inf, transient = FALSE)
  })

  tryCatch({
    # do some stuff
  }, error = function(e) {
    if (grepl("reached elapsed time limit|reached CPU time limit", e$message)) {
      # we reached timeout, apply some alternative method or do something else
    } else {
      # error not related to timeout
      stop(e)
    }
  })

}
查看更多
登录 后发表回答