为什么使用循环计算速度更快?

时间:2020-01-24 22:04:30

标签: r performance for-loop time

更新

如@ Dave2e所指出的那样,将start_time语句(在代码2中)移出for循环将使运行时间与代码1相当。即:

start_time <- Sys.time()
for (j in 1:1) {
   n <- 100
   result <- 1
   for (i in 1:n) {
     result <- result * i
   }
   result
   ## [1] 9.332622e+157
   end_time <- Sys.time()
}
end_time - start_time

for循环是否真的可以提高性能,还是伪造的?


原始帖子:

我有两段代码,如下所示:

代码1:

start_time <- Sys.time()
n <- 100
result <- 1
for (i in 1:n) {
   result <- result * i
}
result
## [1] 9.332622e+157
end_time <- Sys.time()
end_time - start_time

代码2:

for (j in 1:1) {
   start_time <- Sys.time()
   n <- 100
   result <- 1
   for (i in 1:n){
      result <- result * i}
      result
      ## [1] 9.332622e+157
      end_time <- Sys.time()
   }
   end_time - start_time

我期望这两个代码运行类似,但是代码2始终比代码1运行快得多。在我的计算机上,代码1大约需要10 ^ -2秒,而代码2大约需要5 * 10 ^ -6秒。关于如何发生的任何见解?如果仅在整个代码中添加for循环可以减少程序运行时间,以后我将在所有代码中使用它。

1 个答案:

答案 0 :(得分:3)

我认为您的比较不是很可靠。如果不运行多次以获得平均值,就很难说出非常快的代码的相对时序。太多不可控因素会稍微改变运行时间。

我将从下面的基准中得出的结论是,在冗余for循环中封装相当琐碎的计算不会带来很大的伤害,但是任何明显的优势都是琐碎的,可能只是一种效果噪音。

我通过将with_loop放在每个函数中来封装每个代码块(without_loopfunction() { ... })。 (请注意,这意味着我不是基于您的Sys.time()比较的时间,而是基于microbenchmark包中的内置时间。)

microbenchmark软件包更适合基准测试,尤其是对于非常短的计算任务:?microbenchmark::microbenchmark起:

“微基准”可更精确地替代 通常会看到“ system.time(replicate(1000,expr))”表达式。它 努力准确地仅测量花费的时间 评估“ expr”。为了实现这一目标,亚毫秒级(据说 十亿分之一秒)精确的计时功能,最现代的操作 系统提供使用。此外,对 表达式使用C代码完成,以最大程度地减少开销。

library(microbenchmark)
m1 <- microbenchmark(with_loop, without_loop)
library(ggplot2)
autoplot(m1)+scale_y_log10()

分位数(lq,中位数,uq)实际上是相同的。

Unit: nanoseconds
         expr min lq   mean median uq   max neval cld
    with_loop  36 38  48.56     39 40   972   100   a
 without_loop  36 39 177.81     40 41 13363   100   a

enter image description here

没有循环的代码确实平均来说要慢 (即,均值更大),但这几乎完全是由几个异常值驱动的。

现在只关注小于50纳秒的值:

autoplot(m1)+scale_y_log10(limits=c(NA,50))

enter image description here

如果我们再次使用times=1e6(一百万次迭代)执行此操作,则会得到几乎相同的结果:循环的平均值快了3 纳秒(同样可能几乎完全由小驱动上尾巴有波动)。

Unit: nanoseconds
         expr min lq     mean median uq     max neval cld
    with_loop  32 39 86.44248     41 61 2474675 1e+06   a
 without_loop  35 39 89.86294     41 61 2915836 1e+06   a

如果您需要使循环运行十亿次,则相当于运行时间差了3秒。可能不值得担心。