Program Club

R에서 수정시 복사 의미는 정확히 무엇이며 표준 소스는 어디에 있습니까?

proclub 2020. 11. 5. 19:48
반응형

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 내부 에서 찾을 수 없습니다.

질문

내 질문은 두 부분으로 구성됩니다.

  1. 이것은 공식적으로 어디에 기록되어 있습니까?
  2. 수정시 복사는 어떻게 작동합니까?

예를 들어, 약속 이 함수에 전달 되기 때문에 "참조에 의한 전달"에 대해 이야기하는 것이 적절 합니까?


가치 별 호출

R 언어 정의 (섹션에서이 말한다 4.3.3 인수 평가 )

R 인수에서 함수를 호출하는 의미는 call-by-value 입니다. 일반적으로 제공된 인수는 제공된 값과 해당 형식 인수의 이름으로 초기화 된 지역 변수 인 것처럼 작동합니다. 함수 내에서 제공된 인수의 값을 변경해도 호출 프레임의 변수 값에는 영향을주지 않습니다 . [강조 추가]

이것은 copy-on-modify가 작동 하는 메커니즘을 설명하지는 않지만 함수에 전달 된 객체를 변경해도 호출 프레임의 원본에 영향을 미치지 않는다는 것을 언급합니다.

특히 수정시 복사 측면 에 대한 추가 정보 SEXPR 내부 설명서 , 섹션 1.1.2 헤더의 나머지 부분 s 설명에 제공됩니다 . 구체적으로는 [강조 추가]

named필드 설정하고가 액세스 SET_NAMEDNAMED매크로, 값을 0, 1하고 2. R에는 '가치로 호출' 환상이 있으므로 다음과 같은 할당은

b <- a

이 나타납니다가의 사본을 만들려면 a및로 참조b . 그러나 둘 다 a또는 b이후에 변경되지 않으면 복사 할 필요가 없습니다. 실제로 발생하는 것은 새 기호 b가와 동일한 값에 바인딩되고 값 객체 anamed필드가 설정된다는 것입니다 (이 경우 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.

참고URL : https://stackoverflow.com/questions/15759117/what-exactly-is-copy-on-modify-semantics-in-r-and-where-is-the-canonical-source

반응형