Распараллеливание больших симуляций по сетке в R

Я запускаю серию больших симуляций по сетке. Я выполняю моделирование по строкам и обнаружил, что мои функции выборки являются узким местом. Я попытался использовать библиотеки foreach и doMC для ускорения процесса, но обнаружил, что либо параллельный метод работает медленнее, либо мне не удалось закодировать функцию, которая правильно интерпретировалась бы с помощью foreach.

Глядя на некоторые другие сообщения, похоже, что мой подход с использованием foreach может быть ошибочным, поскольку количество выполняемых заданий значительно превышает количество доступных процессоров. Мне интересно, есть ли у кого-нибудь предложения о том, как лучше всего реализовать распараллеливание в моей ситуации. Мои симуляции обычно бывают двух типов. В первом я вычисляю матрицу, которая содержит интервал выборки (строки) для каждого элемента в строке сетки, которую я обрабатываю. Затем я пробую, используя runif (в реальных симуляциях мои строки содержат ~ 9000 ячеек, и я выполняю 10000 симуляций).

#number of simulations per element 
n = 5

#Generate an example sampling interval.
m.int1 <- matrix ( seq ( 1, 20, 1 ), ncol=10, nrow=2 )

#Define a function to sample over the interval defined in m.int1
f.rand1 <- function(a) {
return ( runif ( n, a[1], a[2] ) )
}

#run the simulation with each columns corresponding to the row element and rows 
#the simultions.
sim1 <- round( apply ( m.int1, 2, f.rand1 ) )

Во втором случае я пытаюсь сделать выборку из набора эмпирических распределений, индексированных по столбцам в матрице. Значение элемента grid-row соответствует столбцу для выборки.

#number of simulations per element 
n = 5

#generate a vector represeting a row of grid values 
v.int2 <- round(runif(10,1,3))

#define matrix of data that contains the distributions to be sampled.
m.samples<-cbind(rep(5,10),rep(4,10),rep(3,10))  

f.sample <- function(a) {
return ( sample ( m.samples [ ,a], n, ) )
}

#Sample m.samples indexed by column number.
sim2<- sapply(v.int2,f.sample)

Во втором примере мне удалось использовать foreach () и % dopar% для параллельной работы, но моделирование заняло значительно больше времени, чем последовательный код. В случае с первым примером, приведенным выше, я не смог написать правильную функцию, которая использовала бы распараллеливание foreach. Я поместил код, который использовал во втором случае, просто чтобы продемонстрировать свое мышление, но теперь я понимаю, что мой подход требует слишком больших накладных расходов.

library(foreach)
library(doMC)
registerDoMC(2)

n = 5

#Sample m.samples indexed by column number using parallel method.
sim2.par <- foreach ( i = 1 : length ( v.int2 ), 
    .combine="cbind") %dopar% sample ( 
     m.samples [ , v.int2 [i] ] , n )

Я был бы признателен за некоторые предложения по подходу (и немного кода!), Которые помогут мне эффективно использовать распараллеливание. Опять же, строки, которые я обрабатываю, обычно содержат около 9000 элементов, и мы проводим 10000 симуляций для каждого элемента. Таким образом, мои выходные матрицы моделирования обычно имеют порядок 10000 X 9000. Спасибо за вашу помощь.


person user2059737    schedule 12.02.2013    source источник
comment
В случаях, когда отдельная итерация коротка, накладные расходы могут быть, условно говоря, очень дорогими. Вот почему вы не видите никаких повышений при запуске чего-либо на многих ядрах. Другими словами, работа выполняется так быстро, что на общение уходит больше времени, чем на саму работу.   -  person Roman Luštrik    schedule 12.02.2013


Ответы (2)


Вот небольшое улучшение вашей первой симуляции. Чем больше n, тем больше выигрыш во времени выполнения.

> n <- 1000
> m.int1 <- matrix ( seq ( 1, 20, 1 ), ncol=10, nrow=2 )
> f.rand1 <- function(a) {
+    return(runif(n, a[1], a[2]))
+ }
> system.time(x1 <- replicate(n, round(apply(m.int1, 2, f.rand1))))
   user  system elapsed 
   2.84    0.06    2.95 
> system.time(x2 <- replicate(n, matrix(round(runif(n*10, min = m.int1[1, ], max = m.int1[2, ])), ncol = 10, byrow = TRUE)))
   user  system elapsed 
   2.48    0.06    2.61 
> head(x1[,,1])
     [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
[1,]    1    4    5    7   10   12   13   16   17    20
[2,]    1    3    6    7   10   11   13   16   17    19
[3,]    1    3    6    7   10   12   14   16   18    20
[4,]    2    4    5    7    9   12   14   16   17    19
[5,]    1    4    5    7   10   12   14   16   17    20
[6,]    1    4    6    8    9   11   13   15   18    20
> head(x2[,,1])
     [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
[1,]    2    4    6    7    9   12   14   16   17    20
[2,]    1    3    6    8   10   12   14   15   18    20
[3,]    2    4    5    7    9   11   13   15   17    20
[4,]    2    3    5    7    9   11   14   15   17    19
[5,]    2    3    6    7    9   12   13   16   17    20
[6,]    2    4    6    7   10   12   14   16   17    20
person Roman Luštrik    schedule 12.02.2013

Попробуйте использовать это вместо двухэтапного процесса. Он пропускает шаг apply:

f.rand2 <- function(a) {
  matrix( runif ( n*ncol(a), rep(a[1,], n) , rep(a[2,], n) ), nrow=ncol(a) )
                    }

f.rand2(m.int1)
           [,1]      [,2]      [,3]      [,4]      [,5]
 [1,]  1.693183  1.404336  1.067888  1.904476  1.161198
 [2,]  3.411118  3.852238  3.621822  3.969399  3.318809
 [3,]  5.966934  5.466153  5.624387  5.646181  5.347473
 [4,]  7.317181  7.106791  7.403022  7.442060  7.161711
 [5,]  9.491231  9.656023  9.518498  9.569379  9.812931
 [6,] 11.843074 11.594308 11.706276 11.744094 11.994256
 [7,] 13.375382 13.599407 13.416135 13.634053 13.539246
 [8,] 15.948597 15.532356 15.692132 15.442519 15.627716
 [9,] 17.856878 17.208313 17.804288 17.875288 17.232867
[10,] 19.214776 19.689534 19.732680 19.813718 19.866297

Для меня это вдвое сокращает время:

> system.time(x1 <- replicate(n, round(apply(m.int1, 2, f.rand1))))
   user  system elapsed 
  1.088   0.470   1.550 

> system.time(x1 <- replicate(n, f.rand2(m.int1)))
   user  system elapsed 
  0.559   0.256   0.811 
person IRTFM    schedule 12.02.2013