Performance 为什么R中的循环速度慢?

Performance 为什么R中的循环速度慢?,performance,r,apply,Performance,R,Apply,我知道循环在R中的速度很慢,我应该尝试以矢量化的方式进行操作 但是,为什么?为什么循环速度慢而应用速度快apply调用几个子函数——这似乎并不快 更新:对不起,这个问题不合适。我把矢量化与应用相混淆。我的问题应该是 “为什么矢量化速度更快?”R中的循环速度慢,原因与任何解释语言的速度慢相同:每个 这次行动带来了很多额外的行李 查看(这是调用函数以调用 用户定义的函数)。它将近100行长,表演各种各样的音乐 操作——创建执行环境,将参数分配到 环境等等 想一想,当您在C中调用一个函数时,发生的情况

我知道循环在
R
中的速度很慢,我应该尝试以矢量化的方式进行操作

但是,为什么?为什么循环速度慢而应用速度快
apply
调用几个子函数——这似乎并不快

更新:对不起,这个问题不合适。我把矢量化与应用相混淆。我的问题应该是


“为什么矢量化速度更快?”R中的循环速度慢,原因与任何解释语言的速度慢相同:每个 这次行动带来了很多额外的行李

查看(这是调用函数以调用 用户定义的函数)。它将近100行长,表演各种各样的音乐 操作——创建执行环境,将参数分配到 环境等等

想一想,当您在C中调用一个函数时,发生的情况会少多少(将args推到 堆栈、跳转、弹出参数)

这就是为什么会有这样的时间安排(正如joran在评论中指出的, 它不是真正的
apply
,而是
mean
这很快。
apply
只是普通的旧R代码):

使用循环:0.342秒:

system.time({
    Sum = 0
    for (i in seq_along(A)) {
        Sum = Sum + A[[i]]
    }
    Sum
})
使用总和:不可测量的小:

sum(A)
这有点令人不安,因为,渐进地,这个循环同样好 作为总和;没有实际理由认为它应该是缓慢的;它只是做得更多 每次迭代都需要额外的工作

因此,请考虑:

# 0.370 seconds
system.time({
    I = 0
    while (I < 100000) {
        10
        I = I + 1
    }
})

# 0.743 seconds -- double the time just adding parentheses
system.time({
    I = 0
    while (I < 100000) {
        ((((((((((10))))))))))
        I = I + 1
    }
})

或者,一般来说,解释操作(在任何语言中)有更多的步骤。当然,这些步骤也有好处:你不能做到这一点。
C中的技巧。

循环并不总是很慢,
应用
也很快。下面是一个很好的讨论:

Uwe Ligges和John Fox.R帮助台:我如何避免这种循环或 让它更快?R新闻,8(1):46-502008年5月

在“循环”部分(从第48页开始),他们说:

许多关于R的评论指出,使用循环是一个特别糟糕的主意。这不一定是真的。在某些情况下,编写矢量化代码很困难,或者矢量化代码可能会消耗大量内存

他们进一步建议:

  • 在循环之前将新对象初始化为完整长度,而不是 而不是在循环中增加它们的大小。
  • 不要在循环中做事情 可以在循环之外完成的循环。
  • 不要简单地避免循环 为了避免循环。

他们有一个简单的例子,其中一个
for
循环需要1.3秒,但是
apply
内存不足。

对提出的问题的唯一答案是:如果需要在一组数据上迭代执行某个函数,并且该函数或操作没有矢量化,那么循环并不慢。
for()
循环通常与
apply()
一样快,但可能比
lappy()
调用慢一点。最后一点在本文中已详细介绍,例如,如果设置和操作循环所涉及的代码是循环整体计算负担的重要部分,则适用

为什么许多人认为
for()
循环很慢,是因为用户编写的代码不好,如果您需要扩展/增长对象,这也将涉及到复制,因此您将同时承担复制和增长对象的开销。这不仅限于循环,而且如果您在循环的每次迭代中复制/增长,当然,循环将变慢,因为您将执行许多复制/增长操作

在R中使用
for()
循环的一般习惯用法是,在循环开始之前分配所需的存储,然后填充这样分配的对象。如果遵循该习惯用法,循环不会变慢。这是
apply()
为您管理的,但它只是隐藏在视图之外

当然,如果使用
for()
循环实现的操作存在一个向量化函数,不要这样做。同样地,如果存在向量化函数,也不要使用
apply()
等(例如
apply(foo,2,mean)
最好通过
colMeans(foo)
).

作为比较(不要读太多!):我在R中运行了一个(非常)简单的for循环,在Chrome和IE 8中运行了JavaScript。 请注意,Chrome会编译本机代码,而带有编译器包的R会编译成字节码

# In R 2.13.1, this took 500 ms
f <- function() { sum<-0.5; for(i in 1:1000000) sum<-sum+i; sum }
system.time( f() )

# And the compiled version took 130 ms
library(compiler)
g <- cmpfun(f)
system.time( g() )
#在R2.13.1中,这需要500毫秒

f我的印象是R中的“应用比for循环快得多”有点a。让答案中的
系统。时间
战争开始吧…这里有很多关于这个主题的好信息:作为记录:应用不是矢量化。应用是一个不同的循环结构(如:否)副作用。请参阅讨论@Chase链接到S中的.Loops(S-Plus?)传统上我们的速度很慢。这与R不同;因此,你的问题并不相关。我不知道S-Plus现在的情况如何。我不清楚为什么这个问题被否决了——这个问题在其他地区来到R的人中很常见,应该添加到FAQ中。那么,S-Plus的目的是什么最后一个例子?不要在R中做愚蠢的事情,并期望它能很快完成它们?@Chase我想这是一种说法。是的,我的意思是像C这样的语言与嵌套括号没有速度差异,但R不会优化或编译。还有(),或者{}在循环体中——所有这些事情都涉及到名称查找。或者一般来说,在R中,当你写得更多时,解释器做得更多。我不确定你想用
for()
循环说明什么?它们根本没有做同样的事情。
for()
循环在
A
的每个元素上迭代并求和。
apply()
调用传递整个向量
A[,1]<
> `(` = function(x) 2
> (3)
[1] 2
# In R 2.13.1, this took 500 ms
f <- function() { sum<-0.5; for(i in 1:1000000) sum<-sum+i; sum }
system.time( f() )

# And the compiled version took 130 ms
library(compiler)
g <- cmpfun(f)
system.time( g() )
// In IE8, this took 282 ms
// In Chrome 14.0, this took 4 ms
function f() {
    var sum = 0.5;
    for(i=1; i<=1000000; ++i) sum = sum + i;
    return sum;
}

var start = new Date().getTime();
f();
time = new Date().getTime() - start;