最近,我对Rcpp软件包进行了改进,看到了它的速度提升,特别是在for循环方面。我正在编程一个不断在5D阵列上进行矩阵代数的模拟。一个例子如下:
library(rbenchmark)
Mat<- assign("Mat",array(10,c(10,10,9,4,9)))
Mat2<-matrix(c(runif(16,0,1)),nrow=4,ncol=4)
FuncR<-function(x){
for(i in 1:dim(Mat)[1]){
for(j in 1:dim(Mat)[2]){
for(loc in 1:dim(Mat)[3]){
for(Yr in 1:dim(Mat)[5]){
Mat[i,j,loc,,Yr]<<-floor(x%*%Mat[i,j,loc,,Yr])
}
}
}
}
}
benchmark(FuncR(Mat2))
test replications elapsed relative user.self sys.self user.child sys.child
1 FuncR(Mat2) 100 17.008
1 7.228 0.016 0 0
现在我简要地阅读了一些关于如何使用Rcpp软件包的介绍,在我深入研究之前,我想知道在上述情况下我是否能真正获得一些东西。我问,因为对我来说,作为非程序员,这意味着一些时间投资,所以我想确保它是值得的。复杂数组背后的理性是我的脚本的每一步都在数组的不同维度上进行矩阵乘法,以模拟各种相关事物的开发时间。我愿意接受改进,而不是坚持一种特殊的设置方式。但是,我也无法使用矢量化进行改进
答案 0 :(得分:2)
你应该通过直接使用矩阵乘法来获得与Rcpp类似的性能。
使用aperm
将第4个尺寸放在前面,然后展平为2个尺寸。您可以执行一个%*%
,然后撤消该过程。
> Mat3 <- aperm(Mat, c(4,1,2,3,5))
> dim(Mat3)
[1] 4 10 10 9 9
> dim(Mat3) <- c(4,prod(10,10,9,9))
> Mat4 <- Mat2 %*% Mat3
> dim(Mat4)
[1] 4 8100
> dim(Mat4) <- c(4,10,10,9,9)
> Mat5 <- aperm(Mat4, c(2,3,4,1,5))
仔细检查:
# From your inner loop
> Mat2 %*% Mat[1,1,1,,1]
[,1]
[1,] 6.357168721
[2,] 18.288843057
[3,] 21.215756948
[4,] 10.310288982
> Mat5[1,1,1,,1]
[1] 6.357168721 18.288843057 21.215756948 10.310288982
LGTM。
我在“学术”代码中看到了这种模式,而不是在行业中。为了缩短代码,它确实会牺牲一些内存,因为不再存在乘法。
如果您可以控制代码库的其他部分,则可以考虑更改Mat
对象的内存布局,然后不需要aperms
答案 1 :(得分:1)
使用数据框而不是5D空间中的切片感觉更舒服,所以这是我解决这个问题的方法:
library(dplyr)
melt(Mat) %>%
setNames(c('i','j','loc','k','Yr','value')) %>%
group_by(i,j,loc,Yr) %>%
mutate(out=floor(Mat2 %*% value))
当然,使用data.table
可以获得类似的解决方案。预计dplyr
和data.table
解决方案都会比您的代码更快。
答案 2 :(得分:1)
或者,您可以考虑使用tensor
库:
> require(tensor)
> MT <- tensor(Mat2, Mat, 2, 4)
> dim(MT)
[1] 4 10 10 9 9
> MT[,1,1,1,1]
[1] 6.357168721 18.288843057 21.215756948 10.310288982