R - Как перебрать каждый кусочек 4D матрицы - PullRequest
0 голосов
/ 10 октября 2018

Я использую doMPI в R для распараллеливания сохранения netCDF климатических данных.Эти данные хранятся в R в 4-мерной матрице m, с данными для 6 переменных, в 20000 временных точек, по сетке широты и долготы.Таким образом, 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 позволяет нескольким объектам выполнять итерацию одновременно, поэтому я мог бы даже без проблем иметь индекс времени рядом с временным интервалом матрицы.К сожалению, я не знаю ни одного способа перебора матрицы по временному интервалу.Есть ли способ сделать так, чтобы на каждой итерации t цикла foreach для переменной var я мог иметь переменную data, которая содержит двумерную матрицу m[,,t,var]?

Я уже попробовал приведенный ниже интуитивный подход, но он повторяет каждый отдельный элемент, а не весь временной интервал за раз.

val <- foreach(time.index=1:ntime, slice=m[,,,var], ...

1 Ответ

0 голосов
/ 12 октября 2018

Если вы можете массировать ваши данные в основном процессе R, вы можете попробовать преобразовать каждый 2-мерный срез в big.matrix из пакета bigmemory и использовать его в своих параллельных рабочих.Это было бы полезно, только если время, необходимое для обработки каждого слайса в подчиненном процессе, является значительным.

См. Этот пример и обратите внимание, что вы можете вкладывать 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, или если обработка каждого 2-мерного среза слишком быстрая (да, это может быть проблематично для мультиобработки, см. этот ответ ), возможно, вы можете извлечь трехмерные срезы из ваших данных и использовать .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] и т. д. каждый раз) и убедиться, что в каждом вызове * экспортируется только один чанк1019 *.

...