[Go] Pointer vs Copy of struct

SeongJun·2020년 9월 10일
1

원문 : https://medium.com/a-journey-with-go/go-should-i-use-a-pointer-instead-of-a-copy-of-my-struct-44b43b104963

일반적으로 copy of struct 보다는 pointer를 쓰는게 더 속도가 빠를거라 생각한다.

하지만 위 글은 모든 상황에서 그렇지는 않다는 내용을 다루고 있다.

아래는 그 상황들을 살펴볼건데 두 가지 case를 살펴볼거다.

Intensive allocation of data

다음 struct가 있을때,

type S struct {
   a, b, c int64
   d, e, f string
   g, h, i float64
}

하나는 copy of struct를 반환하고 하나는 pointer를 반환한다.

func byCopy() S {
   return S{
      a: 1, b: 1, c: 1,
      e: "foo", f: "foo",
      g: 1.0, h: 1.0, i: 1.0,
   }
}

func byPointer() *S {
   return &S{
      a: 1, b: 1, c: 1,
      e: "foo", f: "foo",
      g: 1.0, h: 1.0, i: 1.0,
   }
}

여기서 벤치마크를 만들어서 실행하면, (golang testing.B 모듈을 활용해서 만듦)

func BenchmarkMemoryStack(b *testing.B) {
   var s S

   f, err := os.Create("stack.out") // "heap.out"
   if err != nil {
      panic(err)
   }
   defer f.Close()

   err = trace.Start(f)
   if err != nil {
      panic(err)
   }

   for i := 0; i < b.N; i++ {
      s = byCopy()									// byPonter()
   }

   trace.Stop()

   b.StopTimer()

   _ = fmt.Sprintf("%v", s.a)
}
go test ./... -bench=BenchmarkMemoryHeap -benchmem -run=^$ -count=10 > head.txt && benchstat head.txt
go test ./... -bench=BenchmarkMemoryStack -benchmem -run=^$ -count=10 > stack.txt && benchstat stack.txt

copy of struct가 8배 더 빠른 결과를 가져옴.

왜냐?

위에서 만들어진 trace를 보면 확인할 수 있는데, pointer의 경우 heap 영역에 형성되기 때문에 garbage collector가 작동하게 된다.
( GC가 돌아가는 시간 + heap을 할당하는 system call이 합쳐져서 느린듯. )

결론적으로 heap 영역에 할당하는게 stack영역에 할당하는 것 보다 활씬 느리다.
GOMAXPROCS = 1 을 설정한 후 하면 더 느려진다.

Intensive function calls

struct에 빈 method를 추가할 것.

func (s S) stack(s1 S) {}

func (s *S) heap(s1 *S) {}

아래와 같은 벤치마크를 돌리면

func BenchmarkMemoryStack(b *testing.B) {
   var s S
   var s1 S

  s = byCopy()		// byPointer()
   s1 = byCopy()	// byPointer()
   for i := 0; i < b.N; i++ {
      for i := 0; i < 1000000; i++  {
        s.stack(s1)	// s.heap(s1)
      }
   }
}

이때는 pointer로 하는 연산이 약 2배정도 빠르다. (내 컴퓨터에서는 둘이 똑같다...)

왜냐? 앞에서 pointer가 느렸던 이유는 syscall + GC가 동작하기 때문이었는데 이제는 pointer는 할당은 한번만하고 계속 해당 주소를 공유해서 쓰는 반면, stack에서 돌아가는 경우 함수 호출마다 해당 struct를 복사해서 stack을 생성하기 때문에 느려진다.

Conclusion

  • Pointer는 alloc / free 가 잦은 로직에는 사용하지 않는 것이 좋을듯.
  • 반대로 copy of struct는 function call이 잦은 로직에서는 사용하지 않는 것이 좋을듯. (내 컴퓨터에서는 둘이 성능이 비슷했다.)
profile
풀스택 개발자 + 인공지능 대학원

0개의 댓글