어젯밤에 golang 으로 작성한 모듈의 최적화를 진행했는데 생각보다 유의미한 결과를 얻었다. 그 내용을 기록으로 남겨놓는다.

  1. slice 선언시에 cap 파라미터를 통해 slice 내부 배열의 초기 사이즈를 지정해줄 수 있다. slice 의 사이즈를 미리 알 수 있는 상황에서는 이를 통해 배열의 재할당을 최소화할 수 있다. 전체 코드를 뒤져 cap 파라미터를 추가할 수 있는 곳들에 전부 추가해주었다. 하지만 프로파일링 결과 눈에 뛸 만큼의 성능 변화는 없었다.

  2. OOP 하던 습관이 남아서 그런지 그냥 함수로 선언할 수 있는 로직도 구조체로 선언한 로직들이 몇개 있었다. 불필요한 힙메모리 사용을 방지하기 위해 이를 함수로 변경했다. 이 역시 눈에 띄는 변화는 없었다.

  3. byte slice 를 string 으로 변환할 때 unsafe.Pointer 를 이용해 메모리 할당 횟수를 줄였다.

  4. 자주 사용하는 엔티티들을 디스크 기반으로 캐싱해놓은 코드가 있었는데 이를 메모리 기반으로 변경했다. 막상 캐싱되는 사이즈를 보니 메모리에만 올려도 문제가 없겠다고 판단했다. 덕분에 매번 serialize 를 하지 않아도 되었고, CPU 사용량에 의미있는 감소를 확인할 수 있었다.

  5. Uber 에서 만든 automaxprocs 라이브러리를 적용했다.

  6. 기존에 사용 중이던 ratelimit 용 라이브러리를 교체했다. 원래 uber 에서 만든 걸 쓰고 있었는데 알고리즘을 자세히 살펴보니 우리의 요구사항과는 안맞는 부분이 있었다. 처리량이 유의미하게 증가했음을 확인했다.

  7. 자주 생성되는 구조체에 대해서 sync.Pool 을 적용해보았는데 생각했던 것 만큼 성능 향상이 보이지도 않았고 추후에 잘못 다루면 문제가 생길 여지가 보여서 다시 원복했다.