메모리와 성능간의 trade-off , map vs slice
By SeukWon Kang
(업데이트)2d slice 를 추가하고 수정하였습니다.
기존 idpos 라이브러리를 대대적?으로 정리 했습니다.
idpos는 object의 위치를 저장하고 관리하는 목적의 패키지인데 ( 2d 공간에서 object들을 관리 하는 것이 주 목적 )
기존 구현은 pos2objs map[[2]int]idposi.IDPosIList 과 같이 map을 사용해서 구현 하고 있었습니다.
그런데 프로파일링을 해보니 map access에서 대량의 시간이 소비되고 있다는 것을 깨닿고 ( 25% 정도의 cpu 가 사용되고 있었습니다. ) map들을 가능한 줄인 pos2objs []idposi.IDPosIList
내부적으론 [2]int를 int로 변환하는 (xlen*y +x ) hash 함수를 사용하는 slice 기반으로 수정하였습니다. 이렇게 해서 속도의 향상을 성공적으로 이루어 냈습니다만.
서버에서는 문제가 없는데 같은 data struct를 사용하는 loadtester 에서는 메모리 사용량이 너무 많아지는 문제가 생기더군요. 서버는 1개만 을 만드는데 여러 client를 흉내내는 loadtester에서는 *n 만큼이 만들어 지고 결국 사용량이 1000 client당 5Gbyte가 되는 문제가 생깁니다.
개당으로 나누어 보면 5Mbyte정도니까 많다고 보기는 힘든데 수천 client 접속을 테스트 하려니 메모리가 부족합니다.
그래서 결국 하나의 idpos를 4가지 형태로 구현하고 interface를 통해서 필요에 따라 선택할수 있도록 만들었습니다.
idpos1s : 1차원 slice 형태 구현 , 빠른 속도 , 큰 메모리 소비 idpos1m : 1차원 map 형태의 구현 : 적절한 속도, 적절한 메모리 소비 idpos2m : 2차원 map 형태의 구현 : 최초 구현체 idpos2s : 2차원 slice 최종 구현체 , 가장 빠른 속도,
idpostest.go를 사용해서 사용 예와 성능 결과를 볼수 있습니다만 제기계에서 실행한 결과를 보면 ( 1024, 1024 크기의 공간에서 1M 개의 object를 만들어 넣고 이동하고 지우는 테스트입니다.)
2015/03/23 16:42:01 go # 5 init 608.976061ms 1d map add 1.191278885s 1d map move 5.112887863s 1d map del 524.813552ms 1d map 6.829087268s
1d slice add 716.468157ms 1d slice move 3.695569533s 1d slice del 452.854495ms 1d slice 4.864973212s
2d map add 1.239275357s 2d map move 6.039900665s 2d map del 564.989011ms 2d map 7.844246766s
2d slice add 700.968548ms 2d slice move 3.502773576s 2d slice del 422.48469ms 2d slice 4.62630397s
24.789075258s 2015/03/23 16:42:26 go # 5
같은 결과가 나옵니다. 가장 많이쓰고 또 중요한 move 의 성능을 보면 1d map move 5.112887863s 1d slice move 3.695569533s 2d map move 6.039900665s 2d slice move 3.502773576s
정도의 결과가 나옵니다.
마지막으로 재미삼아 만들어본 2d slice가 가장 빠르군요. ;;;
이결과를 바탕으로 서버에서는 2d slice 를 클라이언트에서는 1m 을 쓰도록 수정하였습니다. 테스트를 해보니 loadtest가 1000 connection 에서 메모리 사용량이 5Gbyte에서 2Gbyte정도로 줄어 들었습니다.
며칠간 고민한것이 해결되니 속이 다 시원 하군요. idpostest 코드의 일부는 아래와 같습니다.
//start
idp1m := idpos1m.New(xlen, ylen)
bench(objs, xlen, ylen, idp1m, "1d map")
idp1s := idpos1s.New(xlen, ylen)
bench(objs, xlen, ylen, idp1s, "1d slice")
idp2m := idpos2m.New(xlen, ylen)
bench(objs, xlen, ylen, idp2m, "2d map")
idp2s := idpos2s.New(xlen, ylen)
bench(objs, xlen, ylen, idp2s, "2d slice")
func bench(objs []*posobj, xlen, ylen int, idp idposi.IDPosManI, name string) {
}
//end
위치는 기존과 같은 https://github.com/kasworld/idpos 입니다만 팩키지들은 서브 폴더에 들어 있습니다.