더북(TheBook)

다음은 tracemem( )을 사용하여 리스트 a를 추적하게 한 다음, 리스트 a의 값을 수정하자 a가 복사됨을 보여준다.

> a <- list()
> tracemem(a)
[1] "<0x081cd534>"
> a$b <- c(1, 2, 3)  # 메모리 복사가 발생해 0x07e73df4 주소에 객체가 새로 만들어짐
tracemem[0x081cd534 -> 0x07e73df4]:
> untracemem(a)

‘2.4 벡터’ 절에서 벡터 연산을 설명하면서 벡터 기반 연산을 사용하는 것이 for 등의 반복문을 사용하는 것보다 효율적이라고 한 바 있다. 그 이유 중 하나가 바로 객체가 불변이라는 점이다. 예를 들어, for 문 안에서 벡터의 인자를 하나씩 바꾸는 다음 코드는 v[i] 값을 1씩 증가시킬 때마다 i번째 값이 수정된 벡터를 매번 새로 만들어 v에 할당한다. 따라서 새로운 객체 1,000개를 생성하는 비효율이 발생한다.

> v <- 1:1000
> for (i in 1:1000) {
+ v[i] <- v[i] + 1  # i 번째 값을 바꿀 때마다 새로운 벡터가 생성된다!
+ }

반면 같은 일을 하는 다음 코드는 v 안에 있는 전체 값을 1만큼 증가시킨 객체를 한 개 만든 다음, 이를 v에 할당한다.

> v <- 1:1000
> v <- v + 1

이처럼 벡터 연산이 더 빠른 이유는 이러한 메모리 사용 최적화 문제와도 관련이 있다. 메모리 문제를 확인하는 한 가지 방법은 다음 코드를 실행해보는 것이다.

> rm(list = ls())  # 메모리에 있는 객체들을 삭제
> gc()             # 가비지 컬렉션(Garbage Collection)4을 수행해 사용하지 않는 메모리를 해제
> v <- 1:99999999
> for (i in 1:99999999) {
+   for (j in 1:99999999) {
+     v[j] <- v[j] + 1
+   }
+ }

위 코드를 실행하고 for 문이 수행되는 동안 작업 관리자를 살펴보면 메모리 사용량이 서서히 증가함을 알 수 있다.


4 가비지 컬렉션(쓰레기 수집으로도 번역됨)은 사용이 종료된 객체가 사용하고 있던 메모리를 자동으로 해제하는 기법이다.

신간 소식 구독하기
뉴스레터에 가입하시고 이메일로 신간 소식을 받아 보세요.