R_Language 板


LINE

其实一直想讨论这个议题,只是迟迟没有去写这个 这次就想说来分享一下preallocated memory这件事 很多人会写这样的code: A_list = list() for (i in 1:N){ A_list[[i]] = # give something } 会直接给一个空的list然後开始塞东西 R里面的list其实是一群pointer组成的向量 如果你没有听过pointer 你可当做"指示物"去引导到你要的东西 举例来说: List_1 <- list(a = 1:5, b = 2:4) List_1就含有两个pointer,指向a跟b的储存位置 我们可以从tracemem来看到这点 tracemem回传一个物件储存在记忆体的位置 你可以透过这个去看物件有在记忆中被复制或被搬移等动作 我们来print out List_1的memory位置 cat(tracemem(List_1), '\n') ## <0000000015083EA8> cat(tracemem(List_1[[1]]), '\n') ## <00000000060E97F0> cat(tracemem(List_1[[2]]), '\n') ## <0000000015083EE0> 这样可以看出tracemem(List_1)跟tracemem(List_1[[1]])的位置不同 如果list是沿着物件的大小去储存 那麽我们应该看到List_1的位置跟List_1[[1]]的位置是重叠的 但是这里显示并非如此,因此,我们可以合理推断 list是一群pointer组成的向量 以上如果你学过C++,应该对这个部分比较熟悉 如果不太熟悉,记得这个结果:list是一群pointer的向量 接着我们看如果我们给一个空的list 去做扩充list的动作会发生什麽事情 s1 = list() cat(tracemem(s1), '\n') ## <000000001523AB70> for (i in 1:3) { s1[[i]] = rnorm(i) cat(tracemem(s1), '\n') } ## <00000000061DAC48> ## <00000000067A55C8> ## <000000000AC68AE8> 这里我们可以看到s1的位置不断的被改变 代表说s1其实不断搬移到不同位置上 那我们看看如果我们先设定好list大小的结果 s2 = vector('list', 3) cat(tracemem(s2), '\n') <000000000AD48B58> for (i in 1:3) { s2[[i]] = rnorm(i) cat(tracemem(s2), '\n') cat(tracemem(s2[[i]]), '\n') } ## <000000000AD48B58> ## <000000000AD48B58> ## <000000000AD48B58> 我们可以看到完全没有改变s2的位置,而s2没有被搬移过 那麽没有搬移告诉我们什麽事情? 没有搬移就告诉我们不需要把位置上的东西做复制 不用做复制,那麽我们的操作就不会因为复制而变慢 我提供一个简单的测试去测试有无preallocated memory的回圈速度差异 f_without_preallocated <- function(){ a <- list() for (i in 1:1e6){ a[[i]] <- rnorm(10) } } f_with_preallocated <- function(){ a <- vector('list', 1e6) for (i in 1:1e6){ a[[i]] <- rnorm(10) } } library(rbenchmark) benchmark(f_without_preallocated(), f_with_preallocated(), columns = c("test", "replications", "elapsed", "relative"), order = "relative", replications = 20) ## test replications elapsed relative ## 2 f_with_preallocated() 20 5.92 1.000 ## 1 f_without_preallocated() 20 360.90 60.963 可以看到差到快60倍的速度,因此,建议preallocate memory!! 我的环境:windows 7 64bit, RRO-3.2.0 (2015-04-16), [email protected] list只是其中一个例子 我只是挑大家最常用到的case去做 还有一些案例像是: MaxIter = 1e6 x = c() i = 0 while (iter < MaxIter){ i <- i + 1 x <- c(x, i) if (## some condition) break } 这种不断增长vector x的方式也是十分的消耗记忆体复制的时间 这种不知道长度时最多人用的做法 但是其实可以换成下列这样: MaxIter = 1e6 x = rep(NA_real_, 10) for (i in 1:MaxIter) x[i] <- i if (## some condition) break } x = x[!is.na(x)] 再给一个简单的benchmark: f_while_1 <- function(){ MaxIter = 1e5 x = c() i = 0 while (iter < MaxIter){ i <- i + 1 x <- c(x, i) if (i > 5e4) break } x } f_while_2 <- 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(f_while_1(), f_while_2(), columns = c("test", "replications", "elapsed", "relative"), order = "relative", replications = 20) ## test replications elapsed relative ## 2 f_while_2() 20 1.00 1.00 ## 1 f_while_1() 20 53.17 53.17 可以看出平均消耗的时间可以差到53倍。 这些测试希望可以带给大家一些东西,谢谢观看此篇文章。 里面还有一些操作的议题,像是向量化运算(vectorise)等,有机会再来讨论 有兴趣可以先去读读看R in a Nutshell第24章有提到相关的事情 (详细资讯:R in a Nutshell, Joseph Adler, O'Reilly) 注一:NA有四种类型NA_integer_, NA_real_, NA_complex_ and NA_character_ 分别是整数、带小数的数、复数跟字元的NA类别 不同NA类别会给vector不同类型的储存大小、储存类型 给一个适当的NA也是一个好方法 另外,一般的NA是NA_character_ PS: 有些人会提到sapply, lapply会比较有效率: 但是其实sapply跟lapply只是有preallocate memory的动作 (sapply, lapply可能有其他优化,如果有人知道再麻烦告知我) 会比没有preallocate memory的for-loop快而已 在你要同时进行多个list或是向量操作 还是建议用回圈比较简单做操作,因为时间根本不会差太多 在一些情况下,sapply跟lapply会更慢 (注二) 给一个简单的例子: N = 5e4 st <- proc.time() a11 <- c() a21 <- list() a31 <- c() for (i in 1:N){ a11[i] <- i a21[[i]] <- cumsum(1:i) a31[i] <- sum(1:i) } proc.time() - st ## user system elapsed ## 12.70 0.02 12.75 st <- proc.time() a12 <- sapply(1:N, function(i)i) a22 <- lapply(1:N, function(i) cumsum(1:i)) a32 <- lapply(1:N, function(i) sum(1:i)) proc.time() - st ## user system elapsed ## 7.57 0.02 9.93 st <- proc.time() a13 <- vector('numeric', N) a23 <- vector('list', N) a33 <- vector('numeric', N) for (i in 1:N){ a13[i] <- i a23[[i]] <- cumsum(1:i) a33[i] <- sum(1:i) } proc.time() - st ## user system elapsed ## 7.76 0.00 8.20 这个结果显示sapply最快,有preallocate memory的for-loop差不多 而没有preallocate memory的for-loop最慢 但是这里还有一件事要注意 就是sapply跟有preallocate memory的for-loop并非总是最快 有时候反而会比没有preallocate memory的for-loop最慢 而且在记忆体相对少时 没有preallocate memory的for-loop跑得出来结果 而sapply跟有preallocate memory的for-loop会跑不出来,因为记忆体不足 因此,在这方面需要去衡量你的运算是否需要消耗大量记忆体 如果是,那麽preallocate memory对你有弊无利 这方面需要经验去判断,但是我还是建议初学者多多使用preallocate memory 但是这里有一个有趣的结果(完全超乎预期的结果): 我把上面的程式码做一点改变(下面我用红色标注我做改变的地方) N = 5e4 st <- proc.time() a11 <- c() a21 <- list() a31 <- c() for (i in 1:N){ a11[i] <- i a21[[i]] <- rnorm(round(mean(a11)/10)) a31[i] <- sum(a11) } proc.time() - st ## user system elapsed ## 12.87 0.11 12.98 st <- proc.time() a12 <- sapply(1:N, function(i)i) a22 <- lapply(1:N, function(i) rnorm(round(mean(a12[1:i])/10))) a32 <- sapply(1:N, function(i) sum(a12[1:i])) proc.time() - st user system elapsed 17.41 0.08 17.49 st <- proc.time() a13 <- vector('numeric', N) a23 <- vector('list', N) a33 <- vector('numeric', N) for (i in 1:N){ a13[i] <- i a23[[i]] <- rnorm(round(mean(a13[1:i])/10)) a33[i] <- sum(a13[1:i]) } proc.time() - st user system elapsed 18.75 0.16 18.90 如果你透过了外来变数来计算你其他的值 而且你的外生变数不断的变化大小下 先生成好外生变数,再进行取子集的动作反而会更慢 这个结果,让我满意外的,我还没有想过这个要怎麽解释 可能看有没有人可以帮忙回答这个问题 注二: 我有遇过这种情况,不过目前还不清楚出现的条件为何, 我的猜测是运算的复杂程度,我给一个我手边有的测试: (我只跑了三四次,都是lapply比较慢,有空的人可以跑跑看rbenchmark) N = 1e4 st <- proc.time() a12 <- lapply(1:N, function(i) replicate(10, rnorm(1000))) b <- rnorm(10) a22 <- lapply(1:N, function(i) a12[[i]] %*% b + rnorm(1000)) a32 <- lapply(1:N, function(i) lm(a22[[i]] ~ a12[[i]])) a42 <- lapply(1:N, function(i) fitted(a32[[i]])) a52 <- lapply(1:N, function(i) resid(a32[[i]])) proc.time() - st ## user system elapsed ## 28.63 0.31 28.94 st <- proc.time() a13 <- vector('list', N) a23 <- vector('list', N) a33 <- vector('list', N) a43 <- vector('list', N) a53 <- vector('list', N) b <- rnorm(10) for (i in 1:N){ a13[[i]] <- replicate(10, rnorm(1000)) a23[[i]] <- a12[[i]] %*% b + rnorm(1000) a33[[i]] <- lm(a22[[i]] ~ a12[[i]]) a43[[i]] <- fitted(a32[[i]]) a53[[i]] <- resid(a32[[i]]) } proc.time() - st ## user system elapsed ## 28.1 0.2 28.3 [关键字]: preallocated memory --



※ 发信站: 批踢踢实业坊(ptt.cc), 来自: 123.205.27.107
※ 文章网址: https://webptt.com/cn.aspx?n=bbs/R_Language/M.1437916508.A.C27.html
1F:→ andrew43: 简单说,就是很大的物件可以先规划好它的记忆体大小。07/26 23:49
2F:→ andrew43: 这篇文给了一个明确的证明,指出这在R也是一样有效。07/26 23:50
3F:→ andrew43: 是这样吗?07/26 23:51
应该说尽量养成习惯先给大小。太大也是无法运行。
4F:→ andrew43: 我在学其它语言也有类似的概念,但经验不足的朋友可能没07/26 23:54
matlab满常提醒这一点,如果有接触的话,而且matlab还很强调vectorise。
5F:→ andrew43: 听过这件事情对效率和记忆体有很大的影响。值得了解。07/26 23:54
6F:推 Edster: 其实就是 numeric(1000)跟for(i in 1:1000)I=c(I,0)的差别07/27 01:18
7F:→ Edster: 一个是预先指定记忆体位置 跟 一个不断成长的 vector.07/27 01:19
8F:→ Edster: 每次都指定记忆体位置是需要时间的.07/27 01:20
是的。
9F:→ andrew43: 我所懂的和Edster说的一模一样,但在R中哪些情况可以避07/27 07:48
10F:→ andrew43: 免(即使我自以为已经避免了)就不甚清楚。这篇多少有帮07/27 07:48
11F:→ andrew43: 助到我,像是如何进行检查。07/27 07:49
如果有帮助,再好不过了。
12F:→ andrew43: 我在学习matlab的时候也有学到向量化和记忆体预分配。07/27 07:50
13F:→ andrew43: 大概书是同一本吧! :)07/27 07:51
我没有看过matlab的书,哈哈哈 都是网路上看到的tips for speeding up matlab
14F:→ andrew43: C兄,你 "s1 = vector('list', 3)" 这行是不是写错了?07/27 07:54
15F:→ andrew43: 这样写不是和之後的s2 一样吗07/27 07:54
16F:→ andrew43: 我猜你要写的会不会是 "s1 = vector('list')" ?07/27 07:56
要写s1=list() 当初忘记改程式 结果确是我要的,囧 ※ 编辑: celestialgod (123.205.27.107), 07/27/2015 08:12:14
17F:推 SeaSprite: love this post. almost god like~ 07/30 05:47







like.gif 您可能会有兴趣的文章
icon.png[问题/行为] 猫晚上进房间会不会有憋尿问题
icon.pngRe: [闲聊] 选了错误的女孩成为魔法少女 XDDDDDDDDDD
icon.png[正妹] 瑞典 一张
icon.png[心得] EMS高领长版毛衣.墨小楼MC1002
icon.png[分享] 丹龙隔热纸GE55+33+22
icon.png[问题] 清洗洗衣机
icon.png[寻物] 窗台下的空间
icon.png[闲聊] 双极の女神1 木魔爵
icon.png[售车] 新竹 1997 march 1297cc 白色 四门
icon.png[讨论] 能从照片感受到摄影者心情吗
icon.png[狂贺] 贺贺贺贺 贺!岛村卯月!总选举NO.1
icon.png[难过] 羡慕白皮肤的女生
icon.png阅读文章
icon.png[黑特]
icon.png[问题] SBK S1安装於安全帽位置
icon.png[分享] 旧woo100绝版开箱!!
icon.pngRe: [无言] 关於小包卫生纸
icon.png[开箱] E5-2683V3 RX480Strix 快睿C1 简单测试
icon.png[心得] 苍の海贼龙 地狱 执行者16PT
icon.png[售车] 1999年Virage iO 1.8EXi
icon.png[心得] 挑战33 LV10 狮子座pt solo
icon.png[闲聊] 手把手教你不被桶之新手主购教学
icon.png[分享] Civic Type R 量产版官方照无预警流出
icon.png[售车] Golf 4 2.0 银色 自排
icon.png[出售] Graco提篮汽座(有底座)2000元诚可议
icon.png[问题] 请问补牙材质掉了还能再补吗?(台中半年内
icon.png[问题] 44th 单曲 生写竟然都给重复的啊啊!
icon.png[心得] 华南红卡/icash 核卡
icon.png[问题] 拔牙矫正这样正常吗
icon.png[赠送] 老莫高业 初业 102年版
icon.png[情报] 三大行动支付 本季掀战火
icon.png[宝宝] 博客来Amos水蜡笔5/1特价五折
icon.pngRe: [心得] 新鲜人一些面试分享
icon.png[心得] 苍の海贼龙 地狱 麒麟25PT
icon.pngRe: [闲聊] (君の名は。雷慎入) 君名二创漫画翻译
icon.pngRe: [闲聊] OGN中场影片:失踪人口局 (英文字幕)
icon.png[问题] 台湾大哥大4G讯号差
icon.png[出售] [全国]全新千寻侘草LED灯, 水草

请输入看板名称,例如:BuyTogether站内搜寻

TOP