계산 효율을 높이기 위한 foreach 패키지

패키지 {foreach}는 RevolutionR의 일부로 제공되는 모듈이다. 공짜로 설치해서 사용할 수 있고, {parallel} 보다 훨씬 더 편하기 때문에 대량 계산을 위한 필수품이다.

설치와 사용

#설치
> install.packages("foreach")

#사용
> library(foreach)

인수 하나, 간단한 함수 그리고 list 출력

# 디폴트는 list로 출력하는 것
# foreach() 안에 반복할 인수를 벡터로 넣는다. 이들은 sqrt()에 사용된다.
# %do% 연산자가 foreach()와 sqrt()를 연결한다.

> x <- foreach(i=1:3) %do% sqrt(i)
> x
[[1]]
[1] 1

[[2]]
[1] 1.414214

[[3]]
[1] 1.732051

인수 하나, 간단한 함수 그리고 vector 출력

# .combine 옵션에는 결과를 요약할 함수를 적는다.
> x <- foreach(i=1:3, .combine='c') %do% sqrt(i)
> x
[1] 1.000000 1.414214 1.732051

인수 둘, 함수 작성을 그 자리에서

# foreach에 i와 j를 써서
# 함수의 정의를 { }안에 직접 작성할 수 있다.

> x <- foreach(i=1:10, j=2:11, .combine='c') %do% {
+   i+j*2
+ }
> x
 [1]  5  8 11 14 17 20 23 26 29 32

이것을 c 코드로 쓰면
#include <stdlib.h>

int i=0;
int j=0;
int *x;
int sizeOfArray=10
x=(int *) malloc(sizeOfArray*sizeof(int));
for(i=1; i<11; i++) {
 for(j=2; j<12; j++) {
  x[i-1] = i+j*2
 }
}
...
등등등
-_-;;

가장 쉬운 파이썬 코드로 써도
x=list()
x_append = x.append
for i in range(1,11):
 for j in range(2,12):
  x_append(i+j*2)

R의 foreach가 더 짧고 더 이해하기 쉽다.

.combine='cbind'

# 결합함수로 cbind를 주고 함수의 return을 vector로 한 경우
# 결과물이 matrix 데이터로 출력된다.

> x <- foreach(i=1:4, j=2:5, .combine='cbind') %do% {
+   rnorm(5,i,j)
+ }
> x
      result.1  result.2   result.3  result.4
[1,] 4.8752530  9.023916  7.4250786  6.266347
[2,] 0.4542793  5.595219  2.8839645 13.681196
[3,] 0.4540102 -1.601676 -1.4208312  2.008794
[4,] 1.8348153  4.766759 -0.7042314  2.664544
[5,] 1.3137413  4.400959  1.5666548  6.405306

# 데이터프레임으로 고치려면
> x.df <- data.frame(x)

.combine='+'


> x <- foreach(i=1:4, j=2:5, .combine='+') %do% {
+   rnorm(5,i,j)
+ }
> x
[1] 11.28218 21.26170 16.86678 16.35755 12.56414

물론 사용자가 집합결과를 정의할 수도 있다. 자세한 내용은 foreach 매뉴얼 참조.

병렬처리


우리가 관심을 가지는 것은 foreach의 병렬처리 기능이다.
이때는 %do% 대신에 %dopar%를 쓴다.

주의할 것은 계산이 어차피 빨리 될 것을 병렬처리한다고 해서 더 빨라지지는 않는다. 오히려 데이터를 이리저리 옮기느라 더 느리다. 계산을 몇 시간에 걸쳐, 몇 일에 걸쳐 수행해야 한다면 병렬처리를 적극 고려해야 한다. 당신의 수명을 연장시켜줄 정도로 답답함에서 벗어나게 한다.

간단한 예를 들어 보자.

Intel Celeron quad core 2.16 GHz 컴퓨터다.

foreach에서 병렬처리를 하려면 {doParallel}이 필요하다. 없다면 일단 설치하자.

> install.packages("doSNOW")

> library(doSNOW)
> worker=makeCluster(4) #core수가 4개일 때
> registerDoSNOW(worker)
> system.time(x <- foreach(i=1:4) %do%  rnorm(5000*1000))
   user  system elapsed
   7.61    0.01    7.67
> system.time(x <- foreach(i=1:4) %dopar%  rnorm(5000*1000))
   user  system elapsed
   0.81    1.15    5.25
> stopCluster(worker)
elapsed 시간을 비교해보면 7.67초에서 5.25초로 계산 시간이 개선된다. 연산 시간이 길어질 수록 이 차이는 더 커진다.

병철처리를 하면 사용할 패키지를 다 넘겨줘야 한다. 혹시 forach 안에서 사용해야 할 패키지가 있으면 .packages=c('package name', 'package nme') 같은 형식으로 넘겨주자. 또한, 병렬 처리의 경과를 보고 싶으면 progress 패키지를 설치하고,

library(progress)
pb<-progress_bar$new(total=100)
progress=function(n) pb$tick()
foreach(i=1:100,.options.snow=list(progress=progress)) %dopar % {...}

이러한 방식으로 설정하자.

댓글

이 블로그의 인기 게시물

Bradley-Terry Model: paired comparison models

R에서 csv 파일 읽는 법

xlwings tutorial - 데이터 계산하여 붙여 넣기