为什么R中的循环慢?
我知道R
中的循环很慢,我应该尝试以vector化的方式来做事情。
但为什么? 为什么循环速度慢, apply
速度快? apply
调用几个子function – 这似乎并不快。
更新:我很抱歉,这个问题是不合适的。 我把vector化和apply
混淆了。 我的问题应该是,“为什么vector化速度更快?”
R中的循环速度很慢,原因是任何解释语言都很慢:每次操作都会带来很多额外的行李。
查看R_execClosure
中的eval.c
(这是调用用户定义函数的函数)。 它近100行,执行各种操作 – 创build执行环境,为环境分配参数等。
想想在C中调用一个函数会发生多less事情(将参数push到stack,jump,pop args)。
所以这就是为什么你会得到像这样的时间(正如评论中指出的那样,实际上并不是那么快,而是内部的C循环, mean
说速度很快, apply
只是普通的旧R代码):
A = matrix(as.numeric(1:100000))
使用循环:0.342秒:
system.time({ Sum = 0 for (i in seq_along(A)) { Sum = Sum + A[[i]] } Sum })
使用总和:无法测量的小:
sum(A)
这有点令人不安,因为渐近地说,循环sum
一样好; 没有实际的理由应该是慢的; 每次迭代只做更多的额外工作。
所以考虑:
# 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 } })
(这个例子是由Radford Neal发现的)
因为(
在R中是一个操作符,并且实际上每次使用时都需要查找名称:
> `(` = function(x) 2 > (3) [1] 2
或者,一般来说,解释操作(用任何语言)都有更多的步骤。 当然,这些步骤也提供了好处:你不能这样做 (
C中的把戏
并不总是这样,循环速度慢, apply
速度快。 在R News的2008年5月号有一个很好的讨论:
Uwe Ligges和John Fox。 R帮助台:如何避免这个循环或使其更快? R News,8(1):46-50,2008年5月。
在“循环”部分 (从第48页开始),他们说:
许多有关R状态的评论,使用循环是一个特别糟糕的主意。 这不一定是真的。 在某些情况下,编写向量化的代码是困难的,或者向量化的代码可能消耗大量的内存。
他们还build议:
- 在循环之前将新对象初始化为全长,而不是在循环内增加它们的大小。
- 不要在循环之外完成一个循环。
- 为了避免循环,不要回避循环。
他们有一个简单的例子,其中for
循环需要1.3秒,但apply
运行内存不足。
提出的问题唯一的答案是; 如果你需要做的是迭代一组执行某个函数的数据并且该函数或者操作没有被vector化, 那么循环不会很慢。 一个for()
循环和apply()
一样快,但可能比lapply()
调用慢一点。 最后一点在SO上有很好的介绍,例如在本答案中 ,如果设置和操作循环所涉及的代码是循环总体计算负担的重要部分,则适用。
为什么很多人认为for()
循环很慢,因为他们,用户,正在写错误的代码。 一般情况下(虽然有几个例外),如果你需要扩展/增长一个对象,这也将涉及到复制,所以你既有复制和增长对象的开销。 这不仅限于循环,但是如果在循环的每次迭代中复制/增长,当然循环将会很慢,因为您正在进行许多复制/增长操作。
在R中使用for()
循环的一般用法是在循环开始之前分配所需的存储空间,然后填充这样分配的对象。 如果你遵循这个习惯用法,循环不会很慢。 这是apply()
为你pipe理的东西,但是它只是隐藏而已。
当然,如果使用for()
循环实现的操作存在vector化函数, 则不要这样做 。 同样,如果存在vector化函数, 则不要使用apply()
等(例如apply(foo, 2, mean)
通过colMeans(foo)
可以更好地执行apply(foo, 2, mean)
colMeans(foo)
)。
就像比较一样(不要太多读)!我在R和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() )
@Gavin Simpson:顺便说一下,S-Plus花了1162毫秒。
和JavaScript相同的代码:
// 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;