我遇到的情况是,使用split-apply-combine可能会遇到运行时内存问题。任务是识别所有模拟中的共同元素。
numListFull <- replicate(1000, sample(1:55000, sample(54900:55000),
replace = FALSE))
format(object.size(numListFull), units = "auto", standard = "SI")
# [1] "66 MB"
# Create list of nums shared by all simulations
numListAll <- numListFull[[1]]
numList <- lapply(numListFull[2:length(numListFull)],
function(x){intersect(x, numListAll)})
format(object.size(numList), units = "auto", standard = "SI")
# [1] "65.7 MB"
numListAll <- Reduce(intersect, numList)
format(object.size(numListAll), units = "auto", standard = "SI")
# [1] "166.4 kB"
当复制从300
增加到1000
时,大小为219.9 MB
,219.9 MB
和87.5 kB
。
有时,复制量甚至会超过10000,即后者的10倍。您知道这样做的更好方法来避免计算机中的内存问题吗?
像这样明智吗?
numList <- lapply(split(2:length(numListFull), rep_len(1:100,length(numListFull))),
function(ind){
lapply(numListFull[ind],
function(x){
intersect(x, numListAll)})})
format(object.size(numList), units = "auto", standard = "SI")
# [1] "87.5 MB"
更新:当然,for循环的作用就像没有存储问题的魅力一样,但是以并行化为代价!
答案 0 :(得分:0)
问题在于,您正在将苹果与苹果进行比较;-)
如果我理解正确,那么最终结果应该是仅包含所有样本中存在的项目的列表或向量,对吧?
因为现在,您正在将所有样本与第一个样本进行比较,所以工作量太大。让我们看一个较小的示例,以及您实际存储的内容。
我们将在您的代码中执行相同的操作,除了
numListFull <- replicate(10, sample(1:1, sample(8:10, size=1)))
(旁注:提供size=1
也会加快代码的速度,尽管幅度不大)
无论如何,运行代码后,numList
现在包含第一个样本中不存在的所有值,但是仍然有很多重复的值,并且这些值可能已被删除。
第二次尝试没有什么不同,尽管我不确定您在这里尝试做什么,但您仍将所有示例与第一个示例进行比较。
老实说,我认为for循环可能并不是最糟糕的情况。一旦您知道某些值不在第一个样本中,或者不在第二个样本中,那么将这些值与第三个样本中的值进行比较就没有用了。所以如果
firststep <- intersect(numListFull[[1]], numListFull[[2]])
然后最好的下一步是将numListFull[[3]]
与该firststep
进行比较,而不是再次查看numListFull[[1]]
。这意味着您的结果会不断更新,并且for循环是最好的。
只需跟踪所有样本到该点为止的所有值,而不是存储各种中间结果即可。
可以并行处理,但是随后必须单独计算。因此,将sample1与sample2进行比较,同时将3与4进行比较; 5和6等 我认为这更多是本着处理大数据的方式的精神:在很小的块上进行计算,以便减少可传递的结果。但是将其可视化为二叉树:在每个步骤中,传递的结果最多是其一半。 我认为这就是您在第二个示例中尝试做的事情。至少我明白了:
numListFull <- lapply(split(numListFull,
rep(1:ceiling(length(numListFull)/2), each=2)[seq_len(length(numListFull))]), function(dfs) {
if(length(dfs)==2) {
return(intersect(dfs[[1]], dfs[[2]]))
} else {
# Can be just one
return(dfs[[1]])
}
})
当然这只是一个步骤,但是如果将其放入while循环中,它将持续到长度仅为1。 对于内存,numListFull在每个循环中都会被覆盖,因此垃圾控制始终可以将内存使用量减少到比我们开始时少的数量。
另一方面,调试它可能比在for循环中更难,而且我不确定intersect
中已经发生了多少并行化。无论如何,您必须将lapply
替换为parallel::mclapply
才能获得这些好处,并且如果您是在普通台式机或笔记本电脑上运行设备,那么还会遇到另一个内存问题,即缓存会带来更多好处很难。
在for循环中,处理器可以将相关数据保留在高速缓存中,甚至可以提前获取不久后需要的数据。但是,如果您坚持并行处理,则意味着要在内存的不同部分上进行工作,这意味着必须将所有数据写掉并重新检索。这样可以很好地平衡您使用多核所获得的任何收益。