为什么R在这个随机排列函数上慢?
我是R(Revolution Analytics R)的新手,一直在将一些Matlab函数翻译成R 问题:为什么GRPdur(n)函数这么慢 以下是我在Dell Precision 690、2xQuadcore Xeon 5345@2.33 GHz、Windows 7 64位上获得的信息:为什么R在这个随机排列函数上慢?,r,random,permutation,R,Random,Permutation,我是R(Revolution Analytics R)的新手,一直在将一些Matlab函数翻译成R 问题:为什么GRPdur(n)函数这么慢 以下是我在Dell Precision 690、2xQuadcore Xeon 5345@2.33 GHz、Windows 7 64位上获得的信息: > system.time(GRPdur(10^6)) user system elapsed 15.30 0.00 15.32 > system.time(sample
> system.time(GRPdur(10^6))
user system elapsed
15.30 0.00 15.32
> system.time(sample(10^6))
user system elapsed
0.03 0.00 0.03
这是我在Matlab2011b中得到的
>> tic;p = GRPdur(10^6);disp(toc)
0.1364
tic;p = randperm(10^6);disp(toc)
0.1116
这是我在Matlab2008a中得到的
>> tic;p=GRPdur(10^6);toc
Elapsed time is 0.124169 seconds.
>> tic;p=randperm(10^6);toc
Elapsed time is 0.211372 seconds.
>>
链接:GRPdur是我编写的一个Matlab函数包的一部分,该函数包生成并测试各种随机排列生成器。可在此处单独查看注释:
最初的Durstenfeld Algol程序是,因为您在R-skin中编写了一个c程序
n = 10^6L
p = 1:n
system.time( sample(p,n))
0.03 0.00 0.03
Matlab和S(后来的R)最初都是围绕FORTRAN函数进行数学运算的薄包装
在S/R中,for循环“总是”很慢,但这还可以,因为通常有矢量化的方式来表示问题。此外,R在Fortran或C语言中有数千个函数,可以快速完成更高级的工作。例如,sample
函数与For循环的功能完全相同,但速度要快得多
那么,为什么MATLAB在执行脚本化for循环方面会更好呢?两个简单的原因:资源和优先事项
MathWorks是一家相当大的公司,拥有大约2000名员工。几年前,他们决定优先改进脚本的性能。他们雇佣了一批编译器专家,花了数年时间开发了一个即时编译器(JIT),它将脚本代码转换成汇编代码。他们也做得很好。感谢他们
R是开源的,R核心团队在业余时间致力于改进R。R core的卢克·蒂尔尼(Luke Tierney)努力工作,为R开发了一个编译器包,将R脚本编译成字节码。不过,它并没有将其转换为汇编代码,但效果相当不错。他真了不起
…但是R编译器与MATLAB编译器相比投入的工作量要少得多,因此结果较慢:
system.time(GRPdur(10^6)) # 9.50 secs
# Compile the function...
f <- compiler::cmpfun(GRPdur)
system.time(f(10^6)) # 3.69 secs
回应OP的请求太长,无法发表评论,所以我指的是:
#Create r outside for loop
GRPdur1 <- function(n){
p <- 1:n
k <- seq(n,2,-1)
r <- 1 + floor(runif(length(k)) * k)
for (i in 1:length(k)){
tmp <- p[k[i]];
p[k[i]] <- p[r[i]];
p[r[i]] <- tmp;
}
return(p)
}
library(compiler)
GRPdur2 <- cmpfun(GRPdur1)
set.seed(1)
out1 <- GRPdur(100)
set.seed(1)
out2 <- GRPdur1(100)
#Check the GRPdur1 is generating the identical output
identical(out1,out2)
system.time(GRPdur(10^6))
user system elapsed
12.948 0.389 13.232
system.time(GRPdur2(10^6))
user system elapsed
1.908 0.018 1.910
因此,10x注释(也许并不奇怪,它基于单个
系统。time
运行)是乐观的,但是矢量化比字节编译器的速度快了很多。只是好奇:你在Matlab中尝试过for循环代码吗?因为每次你在r中修改一个对象,创建了一个副本。我可以通过在for循环外正确地向量化R
的创建,然后在其上使用R的字节编译器,将R版本的时间减少大约10倍。@Dieter--我的GRPdur使用for循环,与above@joran请给我们看看你到底做了什么。我通过在循环外生成r(1:n)来“矢量化”Matlab GPRdur。它花费的时间是循环版本的两倍。它还使用了额外的空间——这对于我使用的置换(>10^6)的大小很重要。如果你使用会更好,我保证我会使用@Dieter——我从来没有写过C程序,也永远不会,考虑到我的年龄。我把一个Matlab函数写(翻译)成了R函数。@DerekO'Connor从来不担心,这个专门的程序员可以用任何语言编写C程序(或者,如果你足够大,可以用Fortran程序)。“在S/R中,for循环“总是”很慢:这是一个“一般”的危险神话。”。真正的原因见哈德利的评论。@Dietermene-哈德利在概念上是正确的,但在技术上是错误的。R在修改向量时并不总是复制,也不在问题的for循环中。@Tommy--谢谢,这非常有用。@Tommy--Revo R 2.13.2>system.time(GRPdur(10^6))user 15.41>GRPdurF system.time(GRPdurF(10^6))user 7.28,但仍然比Matlab慢50倍。@DerekO'Connor-是的,这就是MATLAB所做的努力。但根据您的测量,R的示例
比MATLAB的randperm
快3倍。在这两个程序中,这都是正确的方法!谢谢你的时间和努力。这对于初学者来说是非常有用的信息。
f <- function(n) {
p <- integer(n)
p[1] <- 1L
rv <- runif(n, 1, 1:n) # random integer between 1 and k
for (k in 2:n) {
r <- rv[k]
p[k] = p[r] # Swap(p(r),p(k)).
p[r] = k
}
p
}
g <- compiler::cmpfun(f)
system.time(f(1e6)) # 4.84
system.time(g(1e6)) # 0.98
# Compare to Joran's version:
system.time(GRPdur1(10^6)) # 6.43
system.time(GRPdur2(10^6)) # 1.66
system.time(sample.int(10^6)) # 0.03
#Create r outside for loop
GRPdur1 <- function(n){
p <- 1:n
k <- seq(n,2,-1)
r <- 1 + floor(runif(length(k)) * k)
for (i in 1:length(k)){
tmp <- p[k[i]];
p[k[i]] <- p[r[i]];
p[r[i]] <- tmp;
}
return(p)
}
library(compiler)
GRPdur2 <- cmpfun(GRPdur1)
set.seed(1)
out1 <- GRPdur(100)
set.seed(1)
out2 <- GRPdur1(100)
#Check the GRPdur1 is generating the identical output
identical(out1,out2)
system.time(GRPdur(10^6))
user system elapsed
12.948 0.389 13.232
system.time(GRPdur2(10^6))
user system elapsed
1.908 0.018 1.910
library(rbenchmark)
benchmark(GRPdur(10^6),GRPdur2(10^6),replications = 10)
test replications elapsed relative user.self sys.self
1 GRPdur(10^6) 10 127.315 6.670946 124.358 3.656
2 GRPdur2(10^6) 10 19.085 1.000000 19.040 0.222