21

I can run a piece of code for 5 or 10 seconds using the following code:

period <- 10  ## minimum time (in seconds) that the loop should run for
tm <- Sys.time()  ## starting data & time
while(Sys.time() - tm < period) print(Sys.time())

The code runs just fine for 5 or 10 seconds. But when I replace the period value by 60 for it to run for a minute, the code never stops. What is wrong?

Zheyuan Li
  • 71,365
  • 17
  • 180
  • 248
Abdul Basit Khan
  • 646
  • 1
  • 6
  • 19

1 Answers1

33

As soon as elapsed time exceeds 1 minute, the default unit changes from seconds to minutes. So you want to control the unit:

while (difftime(Sys.time(), tm, units = "secs")[[1]] < period)

From ?difftime

 If ‘units = "auto"’, a suitable set of units is chosen, the
 largest possible (excluding ‘"weeks"’) in which all the absolute
 differences are greater than one.

 Subtraction of date-time objects gives an object of this class, by
 calling ‘difftime’ with ‘units = "auto"’.

Alternatively use proc.time, which measures various times ("user", "system", "elapsed") since you started your R session in seconds. We want "elapsed" time, i.e., the wall clock time, so we retrieve the 3rd value of proc.time().

period <- 10
tm <- proc.time()[[3]]
while (proc.time()[[3]] - tm < period) print(proc.time())

If you are confused by the use of [[1]] and [[3]], please consult:


Let me add some user-friendly reproducible examples. Your original code with print inside a loop is quite annoying as it prints thousands of lines onto the screen. I would use Sys.sleep.

test.Sys.time <- function(sleep_time_in_secs) {
  t1 <- Sys.time()
  Sys.sleep(sleep_time_in_secs)
  t2 <- Sys.time()
  ## units = "auto"
  print(t2 - t1)
  ## units = "secs"
  print(difftime(t2, t1, units = "secs"))
  ## use '[[1]]' for clean output
  print(difftime(t2, t1, units = "secs")[[1]])
  }

test.Sys.time(5)
#Time difference of 5.005247 secs
#Time difference of 5.005247 secs
#[1] 5.005247

test.Sys.time(65)
#Time difference of 1.084357 mins
#Time difference of 65.06141 secs
#[1] 65.06141

The "auto" units is very clever. If sleep_time_in_secs = 3605 (more than an hour), the default unit will change to "hours".

Be careful with time units when using Sys.time, or you may be fooled in a benchmarking. Here is a perfect example: Unexpected results in benchmark of read.csv / fread. I had answered it with a now removed comment:

You got a problem with time units. I see that fread is more than 20 times faster. If fread takes 4 seconds to read a file, read.csv takes 80 seconds = 1.33 minutes. Ignoring the units, read.csv is "faster".

Now let's test proc.time.

test.proc.time <- function(sleep_time_in_secs) {
  t1 <- proc.time()
  Sys.sleep(sleep_time_in_secs)
  t2 <- proc.time()
  ## print user, system, elapsed time
  print(t2 - t1)
  ## use '[[3]]' for clean output of elapsed time
  print((t2 - t1)[[3]])
  }

test.proc.time(5)
#   user  system elapsed 
#  0.000   0.000   5.005 
#[1] 5.005

test.proc.time(65)
#   user  system elapsed 
#  0.000   0.000  65.057 
#[1] 65.057

"user" time and "system" time are 0, because both CPU and the system kernel are idle.

Zheyuan Li
  • 71,365
  • 17
  • 180
  • 248