Параллельные вычисления в R (оператор if в foreach %dopar%)

Я хочу обновить свои четыре переменные (Z1, Z2, IVtmp$differror1, IVtmp$differror2) по группам (i), зависящим от оператора if.

foreach(i=unique(IVtmp$scidx)) %dopar% {
  numerator=sum(P1new[IVtmp$scidx==i])+sum(P2new[DATA$scid==i])
  denominator=sum(P1old[IVtmp$scidx==i])+sum(P2old[DATA$scid==i])
  probab=exp(numerator-denominator)

  if (runif(1)<probab){
    Z1[DATA$scid==i]=e1new[DATA$scid==i]
    Z2[DATA$scid==i]=e2new[DATA$scid==i]
    IVtmp$differror1[IVtmp$scidx==i]=differror1new[IVtmp$scidx==i]
    IVtmp$differror2[IVtmp$scidx==i]=differror2new[IVtmp$scidx==i]
    change=change+1
  } else{
    Z1[DATA$scid==i]=e1old[DATA$scid==i]
    Z2[DATA$scid==i]=e2old[DATA$scid==i]
    IVtmp$differror1[IVtmp$scidx==i]=differror1old[IVtmp$scidx==i]
    IVtmp$differror2[IVtmp$scidx==i]=differror2old[IVtmp$scidx==i]
  }
}

Но, похоже, я не мог сделать оператор if внутри цикла foreah. Кто-нибудь может помочь?

Спасибо


person Bobby W Chung    schedule 22.02.2018    source источник
comment
Спасибо, Стив. Итак, что я должен добавить в код?   -  person Bobby W Chung    schedule 23.02.2018


Ответы (1)


Утверждение «если» не является проблемой. Проблема в том, что воркеры не могут напрямую обновлять переменные на мастере. Рабочие могут вычислять только те значения, которые отправляются мастеру, и мастер должен обновлять свои собственные переменные с помощью этих значений.

Я не думаю, что вы получите хорошую производительность, запустив эту проблему параллельно, но вы можете попробовать следующее:

library(doSNOW)
nw <- 4  # choose something reasonable for your computer
cl <- makeSOCKcluster(nw)
registerDoSNOW(cl)
iv <- unique(IVtmp$scidx)

probab <-
  foreach(i=iv, .combine='c') %dopar% {
    numerator=sum(P1new[IVtmp$scidx==i])+sum(P2new[DATA$scid==i])
    denominator=sum(P1old[IVtmp$scidx==i])+sum(P2old[DATA$scid==i])
    exp(numerator-denominator)
  }

for (i in iv) {
  if (runif(1)<probab[i]){
    Z1[DATA$scid==i]=e1new[DATA$scid==i]
    Z2[DATA$scid==i]=e2new[DATA$scid==i]
    IVtmp$differror1[IVtmp$scidx==i]=differror1new[IVtmp$scidx==i]
    IVtmp$differror2[IVtmp$scidx==i]=differror2new[IVtmp$scidx==i]
    change=change+1
  } else{
    Z1[DATA$scid==i]=e1old[DATA$scid==i]
    Z2[DATA$scid==i]=e2old[DATA$scid==i]
    IVtmp$differror1[IVtmp$scidx==i]=differror1old[IVtmp$scidx==i]
    IVtmp$differror2[IVtmp$scidx==i]=differror2old[IVtmp$scidx==i]
  }
}

Это вычисляет probab параллельно, а затем последовательно обновляет структуры данных. Для этого я преобразовал probab в вектор.

Поскольку вычисление probab не занимает много времени, я думаю, что ваша единственная надежда на ускорение — это использовать экстремальное разбиение на фрагменты в параллельном разделе:

library(itertools)
probab <-
  foreach(ivchunk=isplitVector(iv, chunks=nw), .combine='c') %dopar% {
    p <- double(length(ivchunk))
    for (i in ivchunk) {
      numerator=sum(P1new[IVtmp$scidx==i])+sum(P2new[DATA$scid==i])
      denominator=sum(P1old[IVtmp$scidx==i])+sum(P2old[DATA$scid==i])
      p[i] <- exp(numerator-denominator)
    }
    p
  }

При этом используется одна задача на каждого работника для снижения накладных расходов. Это важный метод, но я все еще не уверен, что в этом случае он даст вам достойную производительность по сравнению с последовательным запуском.

person Steve Weston    schedule 23.02.2018
comment
Спасибо, Стив. Это немного улучшает. Еще один общий вопрос о параллельных вычислениях. Сколько ядер я должен запросить? Это как можно больше? - person Bobby W Chung; 25.02.2018
comment
@BobbyWChung detectCores — хорошая отправная точка, но иногда вам следует использовать меньше (другая работа выполняется на компьютере или недостаточно памяти), а иногда вы можете использовать больше (если задача не привязана к процессору). Лучше всего запустить несколько тестов. - person Steve Weston; 26.02.2018
comment
Спасибо, Стив! - person Bobby W Chung; 27.02.2018