为什么R在这个随机排列函数上慢?

为什么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

我是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(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