핵심 요약
- Go의 netip.Addr에는 IPv4를 IPv6로 매핑하는 To6() 메서드가 없어 메인테이너 권장대로 헬퍼 함수를 체이닝했더니 네이티브 대비 8배나 느려졌습니다.
- unsafe 패키지로 메모리를 직접 건드리면 0.87ns로 극적인 성능이 나오지만 권장 코드는 복잡한 어셈블리를 생성해 7.14ns가 걸립니다.
- 결국 컴파일러가 해당 바이트 변환 패턴을 제대로 최적화하도록 직접 Go 컴파일러 해킹에 착수하게 되었습니다.
요약 Go 언어의 netip.Addr 패키지에는 IPv4 매핑 IPv6 주소에서 IPv4 주소를 꺼내는 Unmap() 메서드는 있지만, 반대로 IPv4를 IPv6로 변환하는 Map()이나 To6() 메서드는 공식적으로 제공되지 않습니다. Go 언어 메인테이너들은 이러한 메서드를 표준 라이브러리에 직접 추가하는 대신, 개발자가 netip.AddrFrom16(ip.As16()) 형태로 메서드를 연쇄 호출하면 컴파일러가 이를 알아서 최적화하도록 두어야 한다는 입장을 밝히며 제안을 거절했습니다.
하지만 실제 벤치마크 테스트 결과, Go 메인테이너들이 권장하는 헬퍼 함수 패턴은 연산당 약 7.14ns가 소요되는 반면, 표준 라이브러리를 직접 수정해 필드를 바꾸는 방식은 0.88ns로 무려 8배나 느린 것으로 나타났습니다. 또한 unsafe 패키지를 사용해 netip.Addr 구조체의 내부 메모리 레이아웃을 직접 조작하는 우회 방식을 적용할 경우 표준 라이브러리 수정 방식과 동일한 약 0.87ns의 최적화 성능을 보였습니다.
어셈블리 코드를 분석해 보면, 표준 라이브러리 내장 방식이나 unsafe를 활용한 방식은 레지스터의 식별자(z 필드) 값을 비교한 뒤 단 몇 줄의 인스트럭션만으로 즉시 값을 교체해 반환합니다. 반면 메인테이너 권장 방식은 바이트 엔디언 변환 함수들이 인라인화되면서 불필요한 바이트 쓰기와 읽기 과정이 복잡하게 얽혀 상당한 성능 저하가 발생합니다. 이에 따라 개발자는 Go 컴파일러가 해당 패턴을 효율적인 어셈블리로 인식하고 최적화할 수 있도록 직접 컴파일러를 해킹하고 개선하는 방안을 모색하게 되었습니다.
Sponsored · 광고