중화사전망 - 자전 검색 - Go 언어에서 맵을 사용할 때 큰 맵에 포인터를 저장하지 않도록 합니다.
Go 언어에서 맵을 사용할 때 큰 맵에 포인터를 저장하지 않도록 합니다.
이틀 동안 Mastering Go 에서 GC 장이 map 과 slice 의 가비지 수집 효율을 비교하는 것을 보았습니다. 이 책은 결론만 내고 이유를 설명하지 않아 참을 수가 없어 이 학습 노트를 갖게 되었다. 이렇게 많이 말했는데, 너의 코드를 보여 줘.
이것은 간단한 테스트 절차이다. 문자열 매핑 저장의 효율성은 성형 매핑 GC 저장의 효율성과 몇 배나 다릅니다. 어떤 동창들은 분명히 문자열을 저장했는데 포인터가 없다고 말하는 거 아닌가요? 이것은 Go 언어에서 string 의 기본 구현에 관한 것이다. 소스 코드는 src/runtime/string.go 에 있습니다. String 에는 실제로 데이터에 대한 포인터와 길이 필드가 포함되어 있음을 알 수 있습니다. 여기에는 기본 구현을 포함한 포인터가 포함되어 있는지 확인합니다.
Go 언어의 GC 는 도달 가능한 모든 객체를 재귀적으로 트래버스하고 표시하고 표시 후 참조되지 않은 모든 객체를 정리합니다. 포인터가 스캔되면 끝날 때까지 계속 아래를 내려다보게 됩니다.
Go 언어의 맵은 배열 및 연결된 테이블의 데이터 구조를 기반으로 구현됩니다. 해시 충돌은 최적화 된 zipper 방법을 통해 해결됩니다. 배럴당 8 쌍의 키 값을 저장할 수 있으며, 8 개의 키 값 데이터 뒤에는 오버플로 포인터가 있습니다. 배럴당 최대 8 개의 키 값만 로드할 수 있기 때문입니다. 추가 키 값이 현재 통에 들어가는 경우 오버플로우 버킷이라고 하는 다른 버킷을 만들고 오버플로우 포인터로 연결해야 합니다.
오버플로 포인터로 인해 map 저장 내용에 관계없이 GC 중에 모든 bmap 가 스캔되므로 GC 오버헤드가 크게 발생합니다. 공식 질문' 런타임: 대규모 매핑으로 인해 상당한 GC 일시 중지 # 9477' 에서 이 문제에 대한 논의가 있었다.
무뇌 기계 번역은 다음과 같습니다.
만약 우리가 지도 [K] V 를 가지고 있다면, 여기서 K 와 V 는 모두 포인터를 포함하지 않고, 우리는 스캔 성능을 개선하고자 한다면, 우리는 다음을 할 수 있다.
AllOverflow【】unsafe 를 추가합니다. 포인터 "를 선택하고 모든 오버플로우 통을 저장합니다. 그런 다음 bmap 을 noScan 으로 표시합니다. 이렇게 하면 사용자 데이터를 스캔하지 않기 때문에 매우 빠르게 스캔할 수 있습니다.
사실, ALLOWFLOW 에서 오래된 오버플로우통을 제거해야 하기 때문에 이것은 좀 복잡할 것이다. 또한 hmap 크기를 증가시키므로 데이터를 새로 고쳐야 할 수 있습니다.
마지막으로, 공식적으로 hmap 에 오버플로우 관련 필드를 추가하여 이러한 최적화를 완료합니다. 이것이 바로 구체적인 commit 주소입니다.
그것이 어떻게 실현되었는지 봅시다. 소스 코드는 go1..15, src/cmd/compile/internal/GC/reflect.go 를 기반으로 합니다.
주석에서 알 수 있듯이 맵에 저장된 키 값에 포인터가 없는 경우 (Haspointers 에 의해 결정됨) 오버플로우 필드의 배럴 포인터 대신 uintptr 유형이 사용됩니다. GO 언어에서 uintptr 유형은 포인터 대신 포인터를 저장할 수 있는 정수입니다. 이는 전체 지도를 순회하지 않고 bmap 을 noScan 과 GC 로 표시하는 것과 같습니다. 끊임없이 공부함에 따라, 나는 GO 언어의 많은 모듈들이 너무 정교하게 설계되었다고 점점 더 느끼고 있다.
거의 분명하다, 나는 능력이 제한되어 있다. 만약 타당하지 않다면, 댓글 토론을 환영합니다. 소스 코드 위치는 여전히 그룹의 보스입니다 _