我在R中使用doMPI来并行保存netCDF气候数据。这些数据存储在R的4维矩阵m
中,在纬度和经度网格上,在20000个时间点有6个变量的数据。 因此,m
被索引为m[lon,lat,time,variable]
。根据 netCDF 在磁盘上存储数据的方式,将数据写入磁盘的最有效方法是按时间片。因此,我想为每个变量一次迭代m
一个时间片。目前,我的代码如下所示:
ntime <- 20000
output.vars <- list("rainfall", "snowfallwateq", "snowmelt", "newsnow", "snowdepth", "swe")
for (var.index in seq_along(output.vars)) {
ncout <- nc_open(output.files[var.index], write=TRUE)
val <- foreach(time.index=1:ntime, .packages=c("ncdf4")) %dopar%
{
ncvar_put(ncout, output.vars[[var.index]],
vals=m[,,time.index,var.index],
start=c(1, 1, time.index),
count=c(nlon, nlat, 1))
}
nc_close(ncout)
}
这会不必要地将整个m
矩阵复制到每个工作线程。这占用了非常大的内存量,我需要减少复制的数据量。我从这个答案中想到的是,我可以迭代矩阵的每个时间片,因此在每次迭代时,只有时间片的数据被复制到每个工作线程。foreach
构造允许多个对象同时迭代,因此我甚至可以毫无问题地将时间索引与矩阵时间片放在一起。不幸的是,我不知道有什么方法可以按时间片迭代矩阵。有没有办法做到这一点,使得在变量var
的foreach
循环的每次迭代t
,我可以有一个变量data
来保存二维矩阵m[,,t,var]
?
我已经尝试了下面的直观方法,但它迭代每个单独的元素,而不是一次迭代整个时间片。
val <- foreach(time.index=1:ntime, slice=m[,,,var], ...
如果可以在主 R 进程中处理数据, 您可以尝试将每个二维切片转换为bigmemory
包中的big.matrix
, 并在您的并行工作线程中使用它。 仅当从进程中的每个切片所需的时间很长时,这才有用。
请参阅此示例,请注意,您可以使用%:%
嵌套 2 个foreach
循环
m <- as.numeric(1:16)
dim(m) <- rep(2L, 4L)
# use %do% for sequential processing, without copying the data to parallel workers
big_m <- foreach(i=1L:2L, .combine=c) %:% foreach(j=1L:2L, .combine=list) %do% {
as.big.matrix(m[,,i,j], type="double")
}
descriptors <- lapply(big_m, describe)
# specify .noexport to avoid copying the data to each worker
foreach(m_slice_desc=descriptors, .packages=c("bigmemory"), .noexport=ls(all.names=TRUE)) %dopar% {
# you could even modify the slices in parallel if you wanted
m_slice <- attach.big.matrix(m_slice_desc)
for (i in 1L:2L) {
for (j in 1L:2L) {
m_slice[i,j] <- m_slice[i,j] * 2
}
}
# return nothing
NULL
}
# just to show that the values were modified in place
for (bm in big_m) { print(bm[,]) }
[,1] [,2]
[1,] 2 6
[2,] 4 8
[,1] [,2]
[1,] 18 22
[2,] 20 24
[,1] [,2]
[1,] 10 14
[2,] 12 16
[,1] [,2]
[1,] 26 30
[2,] 28 32
如果你不能/不会使用bigmemory
, 或者如果处理每个二维切片太快 (是的,这对于多处理来说可能是有问题的, 看到这个答案(, 也许您可以从数据中提取三维切片并使用.noexport
一次只复制一个, 像这样:
slices_3d <- lapply(1L:2L, function(i) { m[,,,i] })
foreach(slice_3d=slices_3d, .noexport=ls(all.names=TRUE)) %dopar% {
for (j in 1L:2L) {
slice_2d <- slice_3d[,,j]
# do something
}
# return nothing
NULL
}
我实际上不是100%确定上述内容会阻止复制整个slices_3d
, 如果没有,则可能需要在主 R 进程中手动提取块中的子集 (例如 每次slices_3d[1L:num_parallel_workers]
等等(, 并确保在每次调用foreach
时只导出一个块。