2016-07-21 3 views
9

나는 Go에 HTTP 서버를 구현했다.골란에서 메모리 풀링을 구현하는 방법

각 요청에 대해 특정 구조체에 대해 수백 개의 개체를 만들어야하고 그런 구조체가 10 개 정도 있습니다. 따라서 Go 구현마다 요청이 완료되면 가비지 수집됩니다.

이렇게 각 요청에 대해 많은 양의 메모리가 할당되고 할당이 해제됩니다.

대신 내가 풀에서 받아 요청이 제공 한 후 다시 넣어 것입니다 할당 측면에서 성능뿐만 아니라 요청의 시작 부분에서 또한 GC 측

을 개선하기 위해 풀링 메모리를 구현하고 싶었

풀 구현 측면에서

  1. 특정 구조체 유형의 메모리를 할당하고 할당을 해제하는 방법은 무엇입니까?
  2. 이 메모리가 할당되고 다른 정보가 어떻게 기록되는지 추적하는 방법은 무엇입니까?

메모리 할당 및 할당 해제의 경우 성능을 향상시킬 수있는 다른 제안 사항이 있습니까? 사전에

+5

stdlib에는 [sync.Pool'] (https://golang.org/pkg/sync/#Pool)이 있습니다. 그 외에도 "무료 목록"을 구현하는 것은 Go와 관련이없는 기술입니다. – JimB

답변

14

참고 :

많은 일시적으로 개체에 대한 빠르고 좋은 구현 sync.Pool를 사용하는 것이 좋습니다. 그러나 sync.Pool은 풀링 된 개체가 유지된다는 것을 보증하지 않습니다. 그 문서에서 인용 :

에 저장된 모든 항목을 통지없이 언제든지 자동으로 제거 할 수있다. 이 때 풀이 유일한 참조를 보유하면 항목의 할당이 취소 될 수 있습니다.

그래서 당신은 쓰레기 (귀하의 경우에 따라하는 것이 더 할당이 발생할 수 있음) 채널의 버퍼에 값이 아니기 때문에, 아래 제시된 해결책은, 더 나은 수집 얻을 수있는 Pool에서 개체를하지 않으려면 쓰레기 수거. 객체가 실제 메모리 풀만큼 큰 경우 풀 채널의 오버 헤드가 상각됩니다.


가장 간단한 메모리 풀 "구현"은 버퍼링 된 채널입니다.

큰 개체의 메모리 풀을 원한다고 가정 해 보겠습니다. 비싼 객체의 값에 대한 포인터를 보유한 버퍼링 된 채널을 만들고 필요할 때마다 풀 (채널)에서 값을받습니다. 사용이 끝나면 다시 풀에 넣으십시오 (채널을 보내십시오). 실수로 (예를 들어 공황 상태 인 경우) 객체를 잃어 버리지 않도록하려면 다시 넣을 때 defer 문을 사용하십시오.

은의 우리의 큰 객체의 형태로 사용하자 : 풀을 생성

type BigObject struct { 
    Id  int 
    Something string 
} 

입니다 : 풀의

pool := make(chan *BigObject, 10) 

크기는 단순히 채널의 버퍼의 크기입니다. 많은 goroutines하여 풀을 사용

for i := 0; i < cap(pool); i++ { 
    bo := &BigObject{Id: i} 
    pool <- bo 
} 

: 비싼 객체의 포인터 풀 (이, 마지막에 메모를 선택 참조한다)를 작성

wg := sync.WaitGroup{} 
for i := 0; i < 100; i++ { 
    wg.Add(1) 
    go func() { 
     defer wg.Done() 
     bo := <-pool 
     defer func() { pool <- bo }() 
     fmt.Println("Using", bo.Id) 
     fmt.Println("Releasing", bo.Id) 
    }() 
} 

wg.Wait() 

Go Playground에 시도 .

"풀링 된"모든 개체가 사용 중이면이 구현이 차단됩니다.

var bo *BigObject 
select { 
case bo = <-pool: // Try to get one from the pool 
default: // All in use, create a new, temporary: 
    bo = &BigObject{Id:-1} 
} 

그리고이 경우에는 풀에 다시 넣을 필요가 없습니다 : 당신이하지 않으려면 모두 사용하는 경우, 당신은 새로운 객체를 생성 강제로 select를 사용할 수 있습니다. 아니면 풀에 공간이 있다면 select 다시 차단하지 않고 풀에 모든 다시 넣어 시도 할 수도 있습니다 :

select { 
case pool <- bo: // Try to put back into the pool 
default: // Pool is full, will be garbage collected 
} 

주 : 이전에 풀을 작성

선택 사항입니다. select을 사용하여 풀에서 값을 가져 오거나 다시 넣으려고하면 풀은 처음에는 비어있을 수 있습니다.

요청간에 정보가 누출되지 않도록해야합니다 (예 : 설정되어 있고 다른 요청에 속한 공유 객체에서 필드와 값을 사용하지 않도록하십시오.

+5

이 방법은'sync.Pool'보다 상당히 느립니다. – OneOfOne

+3

@OneOfOne 예,하지만'sync.Pool'은 풀링 된 값의 보존을 보장하지 않습니다. 편집 된 답변을 참조하십시오. – icza

+0

좋은 지적입니다. – OneOfOne

10

이것은 @JimB가 언급 한 sync.Pool 구현입니다. 개체를 풀로 반환하려면 defer의 사용법을 숙지하십시오.

package main 

import "sync" 

type Something struct { 
    Name string 
} 

var pool = sync.Pool{ 
    New: func() interface{} { 
     return &Something{} 
    }, 
} 

func main() { 
    s := pool.Get().(*Something) 
    defer pool.Put(s) 
    s.Name = "hello" 
    // use the object 
} 
+1

오브젝트 인스턴스 작성에 대한 제한이 작성되지 않습니다. –

관련 문제