我尝试报告我在mclapply中遇到的关于不允许大回报值的错误。
显然这个错误已在开发版本中得到修复,但我对响应者的评论更感兴趣:
序列化对象的大小有2GB限制,例如mclapply可以从分叉进程返回,此示例尝试16GB。在R-devel中已经取消了(对于64位版本),但是这种使用非常不寻常且效率很低(该示例需要大约150GB,因为(un)序列化中涉及所有副本)
如果使用mclapply对大数据进行并行计算是低效的,那么有什么更好的方法呢?我做这种事情的需要只是增加,而且我到处都遇到了瓶颈。我已经看到的教程是关于如何使用这些功能的非常基本的介绍,但不一定是如何有效地使用这些功能来管理权衡。这篇文档在这种权衡中有一个小小的模糊:
mc.preschedule:如果设置为“TRUE”,则首先进行计算 到(最多)尽可能多的工作是核心,然后是 工作开始,每项工作可能涵盖多个工作 值。如果设置为“FALSE”,则为每个作业分叉一个作业 'X'的值。前者更适合短期计算或 'X'中的大量值,后者更适合工作 完成时间差异很大而且不太多 'X'的值与'mc.cores'相比
和
默认情况下('mc.preschedule = TRUE')输入'X'被分割成 与核心一样多的部分(目前价值是分散的 顺序地跨核心,即第一个值到核心1,第二个 核心2,...(核心+ 1) - 核心1等的值)然后一个 进程分叉到每个核心并收集结果。
如果没有预先安排,就会为每个值分配一个单独的作业 'X'。确保运行的“mc.cores”作业不超过“mc.cores” 一旦这个数字被分叉,主进程就会等待 让孩子在下一个叉子之前完成
可靠地对这些事情进行基准测试需要花费很多时间,因为有些问题只能在规模上表现出来,然后很难弄清楚究竟发生了什么。因此,更好地了解函数的行为将会有所帮助。
编辑:
我没有具体的例子,因为我经常使用mclapply,并希望更好地了解如何考虑性能影响。虽然写入磁盘可以解决错误,但我认为它不会对必须发生的(反)串行有所帮助,这也必须通过磁盘IO。
一个工作流程如下:
获取一个大的稀疏矩阵M
,并将其以块(例如M1-M100
)写入磁盘,因为M本身不适合内存。
现在说,对于i
中的每个用户I
,Ci
中有M
个列,我想在用户级别添加和聚合。对于较小的数据,这将是相对微不足道的:
m = matrix(runif(25), ncol=5)
df = data.frame(I=sample(1:6, 20, replace=T), C=sample(1:5, 20, replace=T))
somefun = function(m) rowSums(m)
res = sapply(sort(unique(df$I)), function(i) somefun(m[,df[df$I == i,]$C]))
但是对于更大的数据,我的方法是将用户/列的data.frame拆分为不同的data.frames,基于列所在的矩阵M1-M100
,对这些data.frames进行并行循环。 ,读取关联的矩阵,然后遍历用户,提取列并应用我的函数,然后获取输出列表,再次循环并重新聚合。
如果我有一个不能像那样被重新聚合的功能(目前,这不是一个问题),这是不理想的,但我显然用这种方法改变了太多的数据。
答案 0 :(得分:2)
我希望我的回答还为时不晚,但我认为您的示例可以通过bigmemory
包使用共享内存/文件来处理。
让我们创建数据
library(bigmemory)
library(parallel)
#your large file-backed matrix (all values initialized to 0)
#it can hold more than your RAM as it is written to a file
m=filebacked.big.matrix(nrow=5,
ncol=5,
type="double",
descriptorfile="file_backed_matrix.desc",
backingfile="file_backed_matrix",
backingpath="~")
#be careful how to fill the large matrix with data
set.seed(1234)
m[]=c(matrix(runif(25), ncol=5))
#print the data to the console
m[]
#your user-col mapping
#I have added a unique idx that will be used below
df = data.frame(unique_idx=1:20,
I=sample(1:6, 20, replace=T),
C=sample(1:5, 20, replace=T))
#the file-backed matrix that will hold the results
resm=filebacked.big.matrix(nrow=nrow(df),
ncol=2,
type="double",init = NA_real_,
descriptorfile="res_matrix.desc",
backingfile="res_backed_matrix",
backingpath="~")
#the first column of resm will hold the unique idx of df
resm[,1]=df$unique_idx
resm[]
现在,让我们转到您要执行的功能。您撰写了rowSums
但是从您的文字中推断出您的意思是colSums
。我相应改变了。
somefun = function(x) {
#attach the file-backed big.matrix
#it makes the matrix "known" to the R process (no copying involved!)
#input
tmp=attach.big.matrix("~/file_backed_matrix.desc")
#output
tmp_out=attach.big.matrix("~/res_matrix.desc")
#store the output in the file-backed matrix resm
tmp_out[x$unique_idx,2]=c(colSums(tmp[,x$C,drop=FALSE]))
#return a little more than the colSum result
list(pid=Sys.getpid(),
user=x$I[1],
col_idx=x$C)
}
对所有核进行并行计算
#perform colSums using different threads
res=mclapply(split(df,df$I),somefun,mc.cores = detectCores())
检查结果
#processes IDs
unname(sapply(res,function(x) x$pid))
#28231 28232 28233 28234 28231 28232
#users
unname(sapply(res,function(x) x$user))
#1 2 3 4 5 6
#column indexes
identical(sort(unname(unlist(sapply(res,function(x) x$col_idx)))),sort(df$C))
#[1] TRUE
#check result of colSums
identical(lapply(split(df,df$I),function(x) resm[x$unique_idx,2]),
lapply(split(df,df$I),function(x) colSums(m[,x$C,drop=FALSE])))
#[1] TRUE
编辑:我在编辑中发表了您的评论。将结果存储在文件支持的输出矩阵resm
中可以正常工作。
答案 1 :(得分:0)
为了限制适度大N的开销,使用mc.preschedule = TRUE
几乎总是更好(即将工作分成与核心一样多的块)。
看来你的主要权衡是在内存使用和CPU之间。也就是说,您只能进行并行化,直到正在进行的进程最大化RAM。需要考虑的一件事是,不同的工作人员可以在R会话中读取相同的对象而不会重复。因此,只有在并行函数调用中修改/创建的对象才会为每个核心添加内存占用。
如果你最大化内存,我的建议是将你的整个计算划分为多个子工作并循环(例如,使用lapply),在该循环中调用mclapply来并行化每个subjob,并且可能保存subjob输出到磁盘以避免将其全部保存在内存中。