생각해 볼 질문
공유 객체를 복제하거나 순환을 끝없이 도는 일 없이 살아 있는 저장 공간을 어떻게 압축할까요? from-space와 비어 있는 to-space 구분하기 root에서 닿는 순서쌍 포인터만 복사하기 필드를 재귀 복사하기 전에 forwarding 항목 설치하기 같은 옛 셀의 모든 참조에 새 포인터 하나 재사용하기 forwarding된 포인터로 순환 보존하기 회수된 셀 수와 free 포인터의 변화 재기 copy-value는 원자를 그대로 돌려줍니다. 순서쌍 포인터는 먼저 옛 인덱스의 forwarding 항목을 확인합니다. 항목이 없으면 to-space에 placeholder 셀을 하나 할당하고 두 필드를 따라가기 전에 새 포인터를 기록합니다. 그 뒤 car와 cdr를 재귀 복사해 placeholder에 넣습니다. 먼저 기록하는 순서가 공유와 순환을 모두 유한하게 만듭니다.
첫 힙에는 셀 다섯 개가 있지만 root 두 개에서 닿는 셀은 세 개뿐입니다. 복사된 left와 right는 복사된 shared 꼬리 하나를 가리키고 쓰레기 셀 두 개의 forwarding 항목은 #f로 남습니다. 두 번째 힙의 자기 순환은 cdr 복사가 옛 포인터를 다시 만났을 때 이미 기록된 새 포인터를 즉시 재사용합니다.
SICP 코드 UTF-8 4,742 / 1,048,576바이트
( begin
( define ( tagged-list? value tag )
( and ( pair? value ) ( eq? ( car value ) tag ) ) )
( define ( make-vector-memory capacity )
( list ' *vector-memory*
( make-vector capacity ' *unused* )
( make-vector capacity ' *unused* )
( cons ' free 0 ) ) )
( define ( memory-cars memory ) ( list-ref memory 1 ) )
( define ( memory-cdrs memory ) ( list-ref memory 2 ) )
( define ( memory-free-cell memory ) ( list-ref memory 3 ) )
( define ( memory-free memory ) ( cdr ( memory-free-cell memory ) ) )
( define ( set-memory-free! memory value )
( set-cdr! ( memory-free-cell memory ) value ) )
( define ( pair-pointer? value ) ( tagged-list? value ' ptr ) )
( define ( make-pointer index ) ( list ' ptr index ) )
( define ( pointer-index pointer ) ( cadr pointer ) )
( define ( allocate-pair! memory car-value cdr-value )
( let ( ( index ( memory-free memory ) ) )
( if ( >= index ( vector-length ( memory-cars memory ) ) )
( error "vector memory exhausted" index )
( begin
( vector-set! ( memory-cars memory ) index car-value )
( vector-set! ( memory-cdrs memory ) index cdr-value )
( set-memory-free! memory ( + index 1 ) )
( make-pointer index ) ) ) ) )
( define ( memory-car memory pointer )
( vector-ref ( memory-cars memory ) ( pointer-index pointer ) ) )
( define ( memory-cdr memory pointer )
( vector-ref ( memory-cdrs memory ) ( pointer-index pointer ) ) )
( define ( set-memory-car! memory pointer value )
( vector-set! ( memory-cars memory )
( pointer-index pointer )
value ) )
( define ( set-memory-cdr! memory pointer value )
( vector-set! ( memory-cdrs memory )
( pointer-index pointer )
value ) )
( define ( list->memory values memory )
( if ( null? values )
' ( )
( let ( ( tail ( list->memory ( cdr values ) memory ) ) )
( allocate-pair! memory ( car values ) tail ) ) ) )
( define ( memory->list pointer memory )
( if ( null? pointer )
' ( )
( cons ( memory-car memory pointer )
( memory->list ( memory-cdr memory pointer )
memory ) ) ) )
( define ( used-vector-prefix values count )
( define ( loop index result )
( if ( = index count )
( reverse result )
( loop ( + index 1 )
( cons ( vector-ref values index ) result ) ) ) )
( loop 0 ' ( ) ) )
( define ( collect from-memory roots )
( let ( ( to-memory
( make-vector-memory
( vector-length ( memory-cars from-memory ) ) ) )
( forwarding
( make-vector
( vector-length ( memory-cars from-memory ) )
#f ) ) )
( define ( copy-value value )
( if ( not ( pair-pointer? value ) )
value
( let* ( ( old-index ( pointer-index value ) )
( known ( vector-ref forwarding old-index ) ) )
( if known
known
( let ( ( new-pointer
( allocate-pair!
to-memory
' *pending-car*
' *pending-cdr* ) ) )
( vector-set! forwarding
old-index
new-pointer )
( set-memory-car!
to-memory
new-pointer
( copy-value
( memory-car from-memory value ) ) )
( set-memory-cdr!
to-memory
new-pointer
( copy-value
( memory-cdr from-memory value ) ) )
new-pointer ) ) ) ) )
( define new-roots ( map copy-value roots ) )
( list new-roots to-memory forwarding ) ) )
( define from-memory ( make-vector-memory 10 ) )
( define shared
( allocate-pair! from-memory ' shared ' ( ) ) )
( define left
( allocate-pair! from-memory ' a shared ) )
( define right
( allocate-pair! from-memory ' b shared ) )
( define dead-tail
( allocate-pair! from-memory ' dead ' ( ) ) )
( define garbage
( allocate-pair! from-memory ' garbage dead-tail ) )
( define collection
( collect from-memory ( list left right ) ) )
( define roots ( car collection ) )
( define to-memory ( cadr collection ) )
( define forwarding ( caddr collection ) )
( define new-left ( car roots ) )
( define new-right ( cadr roots ) )
( list ( memory-free from-memory )
( memory-free to-memory )
( - ( memory-free from-memory )
( memory-free to-memory ) )
( memory->list new-left to-memory )
( memory->list new-right to-memory )
( equal? ( memory-cdr to-memory new-left )
( memory-cdr to-memory new-right ) )
( vector-ref forwarding ( pointer-index dead-tail ) )
( vector-ref forwarding ( pointer-index garbage ) ) )
) 코드 실행Ctrl/⌘ Enter 파일 열기 코드 저장 코드 복사 편집기 지우기
예제 공유한 살아 있는 그래프를 복사하고 쓰레기 두 셀 버리기 자기 순환을 보존하며 끝내기
실행은 브라우저 안에서 이루어지며 프로그램 결과와 실행 추적을 보여줍니다. 예상 결과 공유 그래프 수집은 (5 3 2 (a shared) (b shared) #t #f #f)를 반환합니다. 순환 수집은 (1 node #t #f)를 반환합니다.
실행 추적에서 볼 점 공유 그래프에서는 옛 shared 포인터의 첫 등장이 새 할당으로 가고 두 번째 등장은 기존 forwarding 항목으로 가는지 보세요. 옛 인덱스 3과 4는 방문하지 않습니다. 순환에서는 cdr 복사가 옛 node에 다시 닿기 전에 forwarding 쓰기가 일어나는지 확인하세요.
직접 해보기 프로그램을 수정하고 결과를 비교해 보세요. shared 꼬리를 직접 가리키는 세 번째 root를 추가하고 복사된 꼬리를 변경하세요. 새 할당 수와 모든 root 관찰을 예상한 뒤 semispace를 바꾸고 셀 하나를 더 할당하세요.
힌트 보기 이미 forwarding된 객체를 가리키는 root는 복사 셀 수를 늘리지 않습니다. 수집 뒤 클라이언트는 낡은 from-space 포인터가 아니라 다시 쓴 root를 사용해야 합니다.