在 PTT R_Language 版有一篇 2015 年的文章「[心得] 預分配記憶體的差異」,比較了 R 迴圈時 List 變數是否預分配記憶體的計算速度差異。作者使用的 R 版本是 Revolution R Open 3.2.0,結論是速度相差 60 倍。但最近幾年 R 改版幅度變化很大,國外有一些新的說法出現,認為 List 變數的記憶體本來就不是連續配置,所以在每個迴圈中逐次增加 List 變數元素,在計算速度的影響不大。
以下是我把該文測試 R 程式用幾個較新 R 版本測試的結果
先看結果。底下表格是 List 變數在迴圈之前是否作「記憶體預分配」的計算時間比較。PRO 3.2.0 是指 Revolution R 公司尚未賣給微軟之前的 Community 版本,已經有一些向量矩陣平行運算的功能:
從上表中可以看出,相較於 PRO 3.2.0 與 R 3.3.2,在 R 3.4.0 版(含)之後,由於內建 JIT 自動編譯,是否作記憶體預分配的差異已經不是很大。
接下來是向量 (Vector) 在迴圈之前是否作記憶體預分配的計算時間比較:
上表可以看出:在迴圈之前有作記憶體預分配的向量計算程式的確快很多,而且在 R 3.4.0 版(含) 之後,速度的提升非常顯著,從 PRO 3.2.0 的 53 倍差距,提升到 452 倍、460 倍的差距。
附註:
測試程式沿用 2015 年 PTT 文章的程式,僅做 functions 名稱的修改
List 變數:
L_NO <- function(){
a <- list()
for (i in 1:1e6){ a[[i]] <- rnorm(10) }
}
L_pre <- function(){
a <- vector('list', 1e6)
for (i in 1:1e6){ a[[i]] <- rnorm(10) }
}
library(rbenchmark)
benchmark(L_NO(), L_pre(),
columns = c("test", "replications", "elapsed", "relative"),
order = "relative", replications = 20)
Vector 變數:
V_NO <- function(){
MaxIter = 1e5
x = c()
i = 0
while (i < MaxIter){
i <- i + 1
x <- c(x, i)
if (i > 5e4)
break
}
x
}
V_pre <- function(){
MaxIter = 1e5
x = rep(NA_real_, MaxIter)
for (i in 1:MaxIter) {
x[i] <- i
if (i > 5e4)
break
}
(x = x[!is.na(x)])
}
library(rbenchmark)
benchmark(V_NO(), V_pre(),
columns = c("test", "replications", "elapsed", "relative"),
order = "relative", replications = 20)