Go 언어 - 이스케이프 분석

검프·2021년 3월 21일
2

Go 언어 학습

목록 보기
1/9

The Ultimate Go Study Guide의 내용을 참고하여 작성했습니다.

이스케이프 분석

이스케이프 분석Escape analysis^{Escape\ analysis}이란 객체의 포인터(참조)가 서브 루틴Subroutine^{Subroutine} 밖으로 전파되는지를 분석하는 기술입니다. 이를 통해서 컴파일러가 최적화를 수행할 때 객체의 메모리를 스택Stack^{Stack}과 힙Heap^{Heap} 중 한 곳에 할당합니다.

type user struct {
    name  string
}

func stayOnStack() user {
    // 스택에 할당됨
    u := user{
      name:  "Gump",
    }

    return u
}

func escapeToHeap() *user {
    // 힙에 할당됨
    u := user{
        name:  "Gump",
    }

    // 이스케이프 분석을 통해 포인터가 리턴됨을 인지하고 객체를 스택이 아닌 힙에 할당함
    return &u
}

위 예제에서 stayOnStack() 함수에서 생성된 객체 u는 리턴 시 객체의 사본이 리턴 됩니다. 이 때문에 stayOnStack() 함수 밖에서는 객체 u를 사용할 수 없습니다. 컴파일 시점에 객체 u의 메모리 크기를 알 수 있기 때문에 컴파일러는 객체 u를 스택에 할당합니다.

반면 escapeToHeap() 함수에서는 객체의 포인터를 리턴합니다. 컴파일러는 이스케이프 분석을 통해 포인터가 리턴됨을 인지하고 객체를 스택이 아닌 힙에 할당하도록 명령합니다.

빌드 시 아래 옵션을 사용하면 이스케이프 분석 정보를 확인할 수 있습니다.

go build -gcflags="-m"

스택이 부족하면 어떻게 될까?

고루틴 스택은 초기에 2KB의 메모리만 할당받습니다. 함수를 호출하면 가장 먼저 스택 공간이 충분한지 확인합니다. 부족할 경우 더 큰 메모리를 할당한 후 값을 복사합니다. 메모리가 부족할 때 마다 값을 복사하는 것은 비효율 적이지만, 고루틴Goroutine^{Goroutine}에 스택을 적게 할당하여 얻는 이점이 더 큽니다.

스택이 커질 수 있기 때문에 하나의 고루틴이 다른 고루틴의 스택 메모리에 대한 포인터를 가질 수 없습니다. 컴파일러가 모든 포인터를 추적하는 것은 지나친 과부하가 되어 지연 시간이 엄청나게 커질 수 있습니다.

이 때문에 고루틴 간에 스택을 공유하지 않습니다.

가비지 컬렉션

힙에 저장한 객체가 더 이상 사용하지 않게 되었을 때 가비지 콜렉터Garbage Collector^{Garbage\ Collector}가 개입합니다. 가비지 콜렉터에게 가장 중요한 것은 페이싱 알고리즘Pacing algorithm^{Pacing\ algorithm}입니다. 가비지 컬렉션에 걸리는 시간을 최소화 하기위해서 어떤 주기와 페이스로 가비지 컬렉션을 실행할 지를 결정해야 합니다.

4MB 힙을 가진 프로그램에서는 가비지 콜렉터가 라이브 힙Live heap^{Live\ heap}을 2MB로 유지하려고 합니다. 라이브 힙이 4MB를 넘어서면 어 큰 힙을 할당해줘야 하기 때문입니다. 가비지 콜렉터는 라이브 힙을 줄여주어야 하기 때문에 페이스는 힙이 얼마나 빠르게 커지는지에 달려있습니다.

GC가 작동할 때는 성능이 떨어질 수밖에 없습니다. 그래야 모든 고루틴이 동시에 작동할 수 있다. GC 역시 가비지 컬렉션 작업을 하는 고루틴들을 실행시키며, 가용 CPU 의 25%를 사용한다. 다음은 runtime.mgc.go 파일의 설정 내용입니다.

// gcBackgroundUtilization is the fixed CPU utilization for background
// marking. It must be <= gcGoalUtilization. The difference between
// gcGoalUtilization and gcBackgroundUtilization will be made up by
// mark assists. The scheduler will aim to use within 50% of this
// goal.
//
// Setting this to < gcGoalUtilization avoids saturating the trigger
// feedback controller when there are no assists, which allows it to
// better control CPU and heap growth. However, the larger the gap,
// the more mutator assists are expected to happen, which impact
// mutator latency.
const gcBackgroundUtilization = 0.25
profile
권구혁

0개의 댓글