R에서 수정시 복사 의미는 정확히 무엇이며 표준 소스는 어디에 있습니까?
가끔씩 나는 R이 수정시 복사 의미론을 가지고 있다는 개념을 발견한다. 예를 들어 Hadley의 devtools wiki에서 .
대부분의 R 객체에는 수정시 복사 의미가 있으므로 함수 인수를 수정해도 원래 값이 변경되지 않습니다.
이 용어를 R-Help 메일 링리스트로 추적 할 수 있습니다. 예를 들어, Peter Dalgaard는 2003 년 7 월에 다음과 같이 썼습니다 .
R은 지연 평가와 약한 동적 유형을 사용하는 기능적 언어입니다 (변수는 원하는대로 유형을 변경할 수 있습니다 : a <-1; a <- "a"허용). 의미 상으로는 최악의 비 효율성을 피하기 위해 일부 최적화 트릭이 구현에 사용되지만 모든 것이 수정시 복사됩니다.
마찬가지로 Peter Dalgaard는 2004 년 1 월에 다음과 같이 썼습니다 .
R에는 수정시 복사 의미론 (원칙적으로 그리고 때로는 실제로)이 있으므로 객체의 일부가 변경되면 객체 자체를 포함하여 객체를 포함하는 모든 항목을 새로운 위치에서 찾아야 할 수 있습니다.
더 나아가 2000 년 2 월 Ross Ihaka는 다음과 같이 말했습니다.
우리는이를 실현하기 위해 상당한 노력을 기울였습니다. 의미론을 "수정시 복사 (필요한 경우)"라고 설명합니다. 복사는 개체가 수정 된 경우에만 수행됩니다. (필요한 경우) 부분은 수정이 비 로컬 변수를 변경할 수 없다는 것을 증명할 수 있다면 복사하지 않고 계속해서 수정한다는 것을 의미합니다.
매뉴얼에 없습니다
아무리 열심히 검색해 도 R 매뉴얼 에서 "복사시 수정"에 대한 참조를 찾을 수 없습니다 . R 언어 정의 나 R 내부 에서 찾을 수 없습니다.
질문
내 질문은 두 부분으로 구성됩니다.
- 이것은 공식적으로 어디에 기록되어 있습니까?
- 수정시 복사는 어떻게 작동합니까?
예를 들어, 약속 이 함수에 전달 되기 때문에 "참조에 의한 전달"에 대해 이야기하는 것이 적절 합니까?
가치 별 호출
R 언어 정의 (섹션에서이 말한다 4.3.3 인수 평가 )
R 인수에서 함수를 호출하는 의미는 call-by-value 입니다. 일반적으로 제공된 인수는 제공된 값과 해당 형식 인수의 이름으로 초기화 된 지역 변수 인 것처럼 작동합니다. 함수 내에서 제공된 인수의 값을 변경해도 호출 프레임의 변수 값에는 영향을주지 않습니다 . [강조 추가]
이것은 copy-on-modify가 작동 하는 메커니즘을 설명하지는 않지만 함수에 전달 된 객체를 변경해도 호출 프레임의 원본에 영향을 미치지 않는다는 것을 언급합니다.
특히 수정시 복사 측면 에 대한 추가 정보 SEXP는 R 내부 설명서 , 섹션 1.1.2 헤더의 나머지 부분 의 s 설명에 제공됩니다 . 구체적으로는 [강조 추가]
named필드 설정하고가 액세스SET_NAMED와NAMED매크로, 값을0,1하고2. R에는 '가치로 호출' 환상이 있으므로 다음과 같은 할당은b <- a이 나타납니다가의 사본을 만들려면
a및로 참조b. 그러나 둘 다a또는b이후에 변경되지 않으면 복사 할 필요가 없습니다. 실제로 발생하는 것은 새 기호b가와 동일한 값에 바인딩되고 값 객체a의named필드가 설정된다는 것입니다 (이 경우2). 개체가 변경 되려고 할 때named필드를 참조합니다. 값은2개체를 변경하기 전에 복제해야 함을 의미합니다. (이는 복제가 필요하다는 의미가 아니라 필요 여부에 관계없이 복제해야 함을0의미합니다 .) 값은SEXP이 개체와 데이터를 공유하는 다른 사람이 없음을 알고 있으므로 안전하게 변경할 수 있음을 의미합니다. . 가치1다음과 같은 상황에 사용됩니다.dim(a) <- c(7, 2)원칙적으로 두 개의 사본이 (원칙적으로) 계산 기간 동안 존재하는 경우
a <- `dim<-`(a, c(7, 2))하지만 더 이상은 아니므로이 경우 복사를 피하기 위해 일부 기본 함수를 최적화 할 수 있습니다.
이것은 객체가 함수에 인수로 전달되는 상황을 설명하지 않지만, 특히 앞서 인용 한 R 언어 정의의 정보를 고려할 때 동일한 프로세스가 작동한다고 추론 할 수 있습니다.
기능 평가의 약속
약속 이 함수에 전달 되었다고 말하는 것이 옳지 않다고 생각합니다 . 인수는 함수에 전달되고 사용 된 실제 표현식은 프라 미스 (및 호출 환경에 대한 포인터 포함)로 저장됩니다. 인수가 평가 될 때만 포인터에 의해 표시된 환경 내에서 검색되고 평가되는 약속에 저장된 표현식이 강제로 알려진 프로세스 입니다.
따라서 나는 이와 관련하여 참조 에 의한 전달 에 대해 이야기하는 것이 옳지 않다고 생각합니다 . R은 값별 호출 의미 체계를 갖지만 인수에 전달 된 값이 평가되고 수정되지 않는 한 복사를 피하려고합니다.
NAMED 메커니즘은 R이 수정시 복사본을 만들어야하는지 여부를 추적 할 수 있도록하는 최적화 (@hadley가 주석에 언급 한대로)입니다. Peter Dalgaard가 논의한대로 NAMED 메커니즘이 정확히 어떻게 작동하는지와 관련된 몇 가지 미묘한 점이 있습니다 ( R Devel 스레드 @mnel에서 질문에 대한 의견에서 인용).
나는 그것에 대해 몇 가지 실험을했고 R은 항상 첫 번째 수정에서 객체를 복사한다는 것을 발견했습니다.
http://rpubs.com/wush978/5916 에서 내 컴퓨터의 결과를 볼 수 있습니다.
제가 실수를했다면 알려주세요. 감사합니다.
개체가 복사되었는지 여부를 테스트하려면
다음 C 코드로 메모리 주소를 덤프합니다.
#define USE_RINTERNALS
#include <R.h>
#include <Rdefines.h>
SEXP dump_address(SEXP src) {
Rprintf("%16p %16p %d\n", &(src->u), INTEGER(src), INTEGER(src) - (int*)&(src->u));
return R_NilValue;
}
2 개의 주소가 인쇄됩니다.
- 데이터 블록의 주소
SEXP - 연속 블록의 주소
integer
이 C 함수를 컴파일하고로드 해 보겠습니다.
Rcpp:::SHLIB("dump_address.c")
dyn.load("dump_address.so")
세션 정보
다음은 sessionInfo테스트 환경입니다.
sessionInfo()
기록 중 복사
먼저 copy on write 속성을 테스트합니다. 즉, R은 수정 된 경우에만 개체를 복사합니다.
a <- 1L
b <- a
invisible(.Call("dump_address", a))
invisible(.Call("dump_address", b))
b <- b + 1
invisible(.Call("dump_address", b))
개체 는 수정시 b복사됩니다 a. R은 copy on write속성을 구현합니다 .
벡터 / 행렬 수정
그런 다음 벡터 / 행렬의 요소를 수정할 때 R이 객체를 복사하는지 테스트합니다.
길이가 1 인 벡터
a <- 1L
invisible(.Call("dump_address", a))
a <- 1L
invisible(.Call("dump_address", a))
a[1] <- 1L
invisible(.Call("dump_address", a))
a <- 2L
invisible(.Call("dump_address", a))
주소는 매번 변경되므로 R이 메모리를 재사용하지 않습니다.
긴 벡터
system.time(a <- rep(1L, 10^7))
invisible(.Call("dump_address", a))
system.time(a[1] <- 1L)
invisible(.Call("dump_address", a))
system.time(a[1] <- 1L)
invisible(.Call("dump_address", a))
system.time(a[1] <- 2L)
invisible(.Call("dump_address", a))
긴 벡터의 경우 R은 첫 번째 수정 후 메모리를 재사용합니다.
또한 위의 예는 객체가 거대 할 때 "제자리에서 수정"이 성능에 영향을 미친다는 것을 보여줍니다.
매트릭스
system.time(a <- matrix(0L, 3162, 3162))
invisible(.Call("dump_address", a))
system.time(a[1,1] <- 0L)
invisible(.Call("dump_address", a))
system.time(a[1,1] <- 1L)
invisible(.Call("dump_address", a))
system.time(a[1] <- 2L)
invisible(.Call("dump_address", a))
system.time(a[1] <- 2L)
invisible(.Call("dump_address", a))
R은 첫 번째 수정에서만 객체를 복사하는 것 같습니다.
이유를 모르겠습니다.
속성 변경
system.time(a <- vector("integer", 10^2))
invisible(.Call("dump_address", a))
system.time(names(a) <- paste(1:(10^2)))
invisible(.Call("dump_address", a))
system.time(names(a) <- paste(1:(10^2)))
invisible(.Call("dump_address", a))
system.time(names(a) <- paste(1:(10^2) + 1))
invisible(.Call("dump_address", a))
The result is the same. R only copies the object at the first modification.
'Program Club' 카테고리의 다른 글
| 왜 std :: cout을 작성해야하고 std :: << (0) | 2020.11.05 |
|---|---|
| 목록 이해력 및 생성기 표현식에서 산출 (0) | 2020.11.05 |
| VS 코드 : "생성 된 코드를 찾을 수 없기 때문에 중단 점이 무시되었습니다."오류 (0) | 2020.11.05 |
| std :: tuple sizeof, 놓친 최적화입니까? (0) | 2020.11.05 |
| datetime 매개 변수를 전달하는 방법은 무엇입니까? (0) | 2020.11.05 |