"멀티 코어"를 사용하기 위해 R 스크립트를 최적화하는 방법


15

CPU가 4 개인 Ubuntu-Lucid PC에서 GNU R을 사용하고 있습니다. 4 개의 CPU를 모두 사용하기 위해 "r-cran-multicore"패키지를 설치했습니다. 패키지 매뉴얼에는 내가 이해하는 실용적인 예제가 없기 때문에 4 개의 CPU를 모두 사용하기 위해 스크립트를 최적화하는 방법에 대한 조언이 필요합니다.

내 데이터 세트는 50,000 개의 행과 1600 개의 열이있는 data.frame (P1이라고 함)입니다. 각 행에 대해 최대, 합계 및 평균을 계산하고 싶습니다. 내 스크립트는 다음과 같습니다.

p1max <- 0
p1mean <- 0
p1sum <-0
plength <- length(P1[,1])
for(i in 1:plength){
   p1max <- c(p1max, max(P1[i,]))
   p1mean <- c(p1mean, mean(P1[i,]))
   p1sum <- c(p1sum, sum(P1[i,]))
}

CPU 4 개를 모두 사용하기 위해 스크립트를 수정하고 실행하는 방법을 알려주시겠습니까?


위의 프로그램에는 오류가 있습니다 : 줄은 "for (i in 1 : plength)"여야합니다
Simon Byrne

당신은 rigth, thx입니다!
Produnis

1
이것은 StackOverflow에 속하지 않습니까?
R_Coholic

1
이것은 StackOverflow에 속합니다. 여기에는 통계 질문이 없습니다. 일반적인 프로그래밍 질문입니다.
JD Long

답변:


11

foreachdoMC를 사용하십시오 . 자세한 설명은 여기를 참조하십시오 . 당신의 스크립트는 줄을 거의 변경하지 않습니다

for(i in 1:plength){

로 변경되어야합니다

foreach(i=1:plength) %dopar% { 

이 패키지를 사용하는 멀티 태스킹 스크립트의 전제 조건은 다음과 같습니다.

library(foreach)
library(doMC)
registerDoMC()

주의 사항. 설명서에 따르면 GUI에서 사용할 수 없습니다.

문제는 멀티 태스킹이 정말로 필요합니까? data.frame은 약 1.2GB의 RAM을 사용하므로 메모리에 맞아야합니다. 따라서 간단히 apply를 사용할 수 있습니다 :

p1smry <- apply(P1,1,summary)

결과는 각 행의 요약이 포함 된 행렬이됩니다.

패키지 멀티 코어에있는 mclapply 함수를 사용할 수도 있습니다 . 그러면 스크립트는 다음과 같습니다.

loopfun <- function(i) {
     summary(P1[i,])
}

res <- mclapply(1:nrow(P1),loopfun)

그러면 i 번째 요소가 i 번째 행의 요약이되는 목록이 반환됩니다. sapply를 사용하여 행렬로 변환 할 수 있습니다

mres <- sapply(res,function(x)x)

대단히 감사합니다. "적용"을 사용하면 스크립트를 최적화 할 수 있습니다. 방금 메시지를 전달하기 위해 스크립트를 최소한의 예제로 사용했습니다 ... 많이, 당신의 대답은 내가 찾던 것입니다!
Produnis

15

둘 이상의 코어를 사용하는 방법에 대한 답변이 이미 있지만 실제 문제는 루프를 작성하는 방식에 있습니다. 루프가 반복 될 때마다 결과 벡터 / 객체를 확장하지 마십시오 . 이 작업을 수행하면 R이 결과 벡터 / 오브젝트를 복사하고 시간이 걸리는 모든 것을 확장하도록 강제합니다. 대신, 루프를 시작하기 전에 충분한 저장 공간을 미리 할당하고 진행하십시오. 예를 들면 다음과 같습니다.

set.seed(1)
p1 <- matrix(rnorm(10000), ncol=100)
system.time({
p1max <- p1mean <- p1sum <- numeric(length = 100)
for(i in seq_along(p1max)){
   p1max[i] <- max(p1[i,])
   p1mean[i] <- mean(p1[i,])
   p1sum[i ]<- sum(p1[i,])
}
})

   user  system elapsed 
  0.005   0.000   0.005

또는 다음을 통해 이러한 작업을 수행 할 수 있습니다 apply().

system.time({
p1max2 <- apply(p1, 1, max)
p1mean2 <- apply(p1, 1, mean)
p1sum2 <- apply(p1, 1, sum)
})
   user  system elapsed 
  0.007   0.000   0.006 

그러나 이것은 루프를 올바르게 수행하는 것보다 빠르지 않으며 때로는 느립니다.

그러나 항상 벡터화 된 코드를 찾으십시오. 다음을 사용하여 행 합 수단을 할 수있는 rowSums()rowMeans()빠른 루프 또는 하나 이상있는 apply버전 :

system.time({
p1max3 <- apply(p1, 1, max)
p1mean3 <- rowMeans(p1)
p1sum3 <- rowSums(p1)
})

   user  system elapsed 
  0.001   0.000   0.002 

내가 베팅 한 사람이라면, foreach()매트릭스에 대한 속도 테스트에서 뛰 거나 다른 멀티 코어 옵션을 언급하는 세 번째 접근법에 돈을 벌게 될 것입니다. 서로 다른 CPU 코어에서 생성 된 별도의 프로세스

업데이트 : @ shabbychef의 의견에 따라 한 번만 합계를 계산하고 평균 계산에 재사용하는 것이 더 빠릅니까?

system.time({
    p1max4 <- apply(p1, 1, max)
    p1sum4 <- rowSums(p1)
    p1mean4 <- p1sum4 / ncol(p1)
    })

   user  system elapsed 
  0.002   0.000   0.002

이 테스트 실행에는 없지만 철저하지는 않습니다.


FWIW에서 Matlab은 사전 할당 및 확장 벡터와 관련하여 동일한 문제가 있으며 고전적인 코드 인 '루퍼'입니다. 베팅 외에도 rowSums행 평균을 계산하는 데 결과를 사용하는 것이 더 빠를 수 있습니다 ( 예 : Na 또는 NaN 과 관련하여 누락 된 경우 제외 ). 세 번째 접근 방식의 코드는 각 열을 두 번 합칩니다 .
shabbychef

@shabbychef 당신은 놀랄 것입니다 (내 편집 된 답변 참조). 예 합계는 개념적으로 두 번 계산되지만 rowSumsrowMeans고도의 컴파일 된 코드와 우리가 한 번만 금액을 계산에 이득을 최적화, 우리는 해석 코드에서 평균 계산을하고 다시 느슨한.
복원 모니카

@Gavin Simpson : 그렇게 빠르지는 않습니다 : 대신 system.time({ for (iii in c(1:1000)) { p1max3 <- apply(p1, 1, max) p1mean3 <- rowMeans(p1) p1sum3 <- rowSums(p1) } })에 비슷하게 시도하십시오 system.time({ for (iii in c(1:1000)) { p1max4 <- apply(p1, 1, max) p1sum4 <- rowSums(p1) p1mean4 <- p1sum4 / ncol(p1) } }); 합계를 다시 계산하지 않는 버전은 내 컴퓨터에서 1.368 초가 걸립니다. 1.396이 필요한 것. 다시, 철저하지만, 더 매력적이지 않은 ...
shabbychef

@shabbychef 우리는 또는 강제적하지 무엇 ;-) 사실, 당신의 더 엄격한 시뮬레이션 같은 것을 내 주요 지점을 강화에 다른 생각이 있어야 rowMeans하고 rowSums효율적으로 최적화 된 컴파일 된 코드에서 구현 그들이 이길 어려울 것됩니다.
복원 모니카-G. 심슨

@Gavin Simpson. 실제로 내 예제의 문제점은 대부분의 시간이 적용 부분에서 최대 값을 계산하는 데 걸린다는 것입니다. c와 같은 c 기반 벡터화 함수는와 같은 rowMean범용 R 도구를 통해 이길 수 없다는 것에 동의합니다 *apply. 그러나, 당신은 10000 개 숫자를 합계 빠르다는 것을 시사하는 것 두 번 을 통해 rowMean하고 rowSum오히려 한 번만 사용 R의 내장 분할 연산자보다. R에 효율성 문제가 있다는 것을 알고 있습니다 ( 예 : 최근 중괄호 대 괄호 문제 발견).
shabbychef

1

강설 패키지를 살펴보십시오 . 그와 함께 많은 예 ...

R과 병렬 처리에 대해 배우지 않고 특정 코드의 속도를 높이려면이 작업을 수행해야합니다

P1 = matrix(rnorm(1000), ncol=10, nrow=10
apply(P1, 1, max)
apply(P1, 1, mean)
apply(P1, 1, sum)

내 스크립트를 수정하도록 도와주세요 ...
Produnis

2
그것들은 단지 당신에게서 고리를 숨기고 있습니다. @Produnis 코드의 실제 문제는 루프의 각 반복마다 결과 벡터가 확장되기 때문에 강제 복사가 진행된다는 것입니다.
복원 모니카

강설 패키지는 "케이크"라고 말하는 것처럼 Gavin의 솔루션을 확장 할 수 있습니다. 패키지에는 멀티 코어를 수행하도록 수정 된 많은 적용 기능이 있습니다. apply 함수의 경우 sfApply (<appar 인수는>)를 사용합니다. 강설량도 잘 정리되어 있습니다. 멀티 코어 프로세서에서이를 수행하기 위해 추가 소프트웨어가 필요하지 않다는 점을 지적해야합니다. sfLapply 예제는 stackoverflow.com/questions/4164960/… 를 참조하십시오 .
로마 Luštrik
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.