루프가 느리고 R
대신 벡터화 된 방식으로 작업을 수행해야 한다는 것을 알고 있습니다.
하지만 왜? 루프가 느리고 apply
빠른 이유는 무엇 입니까? apply
몇 가지 하위 기능을 호출합니다. 빠르지 않은 것 같습니다.
업데이트 : 죄송합니다. 질문이 잘못되었습니다. 벡터화와 apply
. 내 질문은,
“벡터화가 더 빠른 이유는 무엇입니까?”
답변
R의 루프는 모든 해석 언어가 느린 것과 같은 이유로 느립니다. 모든 작업에는 많은 추가 수하물이 있습니다.
R_execClosure
in을eval.c
보십시오 (이것은 사용자 정의 함수를 호출하기 위해 호출되는 함수입니다). 거의 100 줄에 달하며 실행을위한 환경 생성, 환경에 인수 할당 등 모든 종류의 작업을 수행합니다.
C에서 함수를 호출 할 때 얼마나 덜 발생하는지 생각해보십시오 (인수를 스택, 점프, 팝 인수로 푸시).
그래서 당신은 다음과 같은 타이밍을 얻습니다 (조란이 주석에서 지적했듯이 실제로 apply
는 빠르지 않습니다 ; 내부 C 루프 mean
는 빠릅니다. apply
단지 일반적인 오래된 R 코드입니다) :
A = matrix(as.numeric(1:100000))
루프 사용 : 0.342 초 :
system.time({
Sum = 0
for (i in seq_along(A)) {
Sum = Sum + A[[i]]
}
Sum
})
합계 사용 : 측정 할 수 없을 정도로 작음 :
sum(A)
점근 적으로 루프가 다음과 같이 훌륭하기 때문에 약간 당황 스럽습니다. sum
. 느려 야 할 실질적인 이유가 없습니다. 반복 할 때마다 더 많은 추가 작업을 수행하는 것입니다.
따라서 다음을 고려하십시오.
# 0.370 seconds
system.time({
I = 0
while (I < 100000) {
10
I = I + 1
}
})
# 0.743 seconds -- double the time just adding parentheses
system.time({
I = 0
while (I < 100000) {
((((((((((10))))))))))
I = I + 1
}
})
(그 예는 Radford Neal에 의해 발견되었습니다 )
때문에 (
R은 운영자이며, 실제로 당신이 그것을 사용할 때마다 조회의 이름을 필요로 :
> `(` = function(x) 2
> (3)
[1] 2
또는 일반적으로 해석 된 작업 (모든 언어)에는 더 많은 단계가 있습니다. 물론 이러한 단계는 이점도 제공합니다 . C 에서는 이러한 (
트릭을 수행 할 수 없습니다 .
답변
루프가 항상 느리고 apply
빠르지는 않습니다. R News 2008 년 5 월호에 이에 대한 좋은 토론이 있습니다 .
Uwe Ligges와 John Fox. R 헬프 데스크 : 어떻게이 루프를 피하거나 더 빠르게 만들 수 있습니까? R News, 8 (1) : 46-50, 2008 년 5 월.
“루프!”섹션에서 (48 페이지부터 시작) 그들은 다음과 같이 말합니다.
R에 대한 많은 의견은 루프를 사용하는 것이 특히 나쁜 생각이라고 말합니다. 이것은 반드시 사실이 아닙니다. 경우에 따라 벡터화 된 코드를 작성하기 어렵거나 벡터화 된 코드가 엄청난 양의 메모리를 소비 할 수 있습니다.
그들은 또한 다음을 제안합니다.
- 새 객체를 루프 내에서 크기를 늘리는 대신 루프 전에 전체 길이로 초기화합니다.
- 루프 외부에서 수행 할 수있는 작업을 루프에서 수행하지 마십시오.
- 단순히 루프를 피하기 위해 루프를 피하지 마십시오.
for
루프에 1.3 초가 걸리지 만 apply
메모리가 부족한 간단한 예가 있습니다.
답변
제기 된 질문에 대한 유일한 답변은 다음과 같습니다. 루프는 아닙니다 느린 경우 당신이해야 할 것이 몇 가지 기능을 수행하는 데이터 세트와 그 기능에 대해 반복을하거나 작업이 벡터화되지 않습니다. for()
루프로, 일반적으로 빠른 같이 될 것입니다 apply()
가능성이 조금 더 소요보다하지만 lapply()
전화. 마지막 포인트는 잘 예를 들면, SO에 덮여 대답 하고, 설정 및 동작에 대한 코드 적용된다 루프 의 전반적인 계산 부담의 상당 부분입니다 루프 .
많은 사람들이 for()
루프가 느리다고 생각하는 이유 는 사용자가 잘못된 코드를 작성하기 때문입니다. 일반적으로 (몇 가지 예외가 있지만) 개체를 확장 / 확장해야하는 경우에도 복사가 포함되므로 개체 를 복사 하고 늘리는 오버 헤드가 모두 발생 합니다. 이것은 루프에만 국한된 것이 아니라 루프의 각 반복에서 복사 / 증가하는 경우 많은 복사 / 증가 작업이 발생하기 때문에 루프가 느려질 것입니다.
for()
R에서 루프 를 사용하는 일반적인 관용구 는 루프가 시작되기 전에 필요한 스토리지를 할당 한 다음 할당 된 객체를 채우는 것입니다. 이 관용구를 따르면 루프가 느려지지 않습니다. 이것은 apply()
당신을 위해 관리하는 것이지만보기에서 숨겨져 있습니다.
물론, for()
루프 로 구현하는 연산에 대해 벡터화 된 함수가 존재한다면 그렇게하지 마십시오 . 마찬가지로 벡터화 된 함수가 있으면 etc를 사용 하지 마십시오apply()
(예 : apply(foo, 2, mean)
를 통해 더 잘 수행됨 colMeans(foo)
).
답변
비교처럼 (너무 많이 읽지 마세요!) : 저는 R에서 (매우) 간단한 for 루프를 실행했고 Chrome 및 IE 8에서는 JavaScript에서 실행했습니다. Chrome은 네이티브 코드로 컴파일하고 R은 컴파일러로 컴파일합니다. 패키지는 바이트 코드로 컴파일됩니다.
# In R 2.13.1, this took 500 ms
f <- function() { sum<-0.5; for(i in 1:1000000) sum<-sum+i; sum }
system.time( f() )
# And the compiled version took 130 ms
library(compiler)
g <- cmpfun(f)
system.time( g() )
@Gavin Simpson : Btw, S-Plus에서 1162ms가 걸렸습니다 …
그리고 JavaScript와 “동일한”코드 :
// In IE8, this took 282 ms
// In Chrome 14.0, this took 4 ms
function f() {
var sum = 0.5;
for(i=1; i<=1000000; ++i) sum = sum + i;
return sum;
}
var start = new Date().getTime();
f();
time = new Date().getTime() - start;