Go 앱 최적화
이 가이드에서는 프로필 데이터를 수집하도록 구성된 의도적으로 비효율적인 Go 애플리케이션을 배포합니다. Profiler 인터페이스를 사용하여 프로필 데이터를 보고 잠재적 최적화를 파악합니다. 그런 다음 애플리케이션을 수정 및 배포하고 수정 효과를 평가합니다.
시작하기 전에
- Sign in to your Google Cloud account. If you're new to Google Cloud, create an account to evaluate how our products perform in real-world scenarios. New customers also get $300 in free credits to run, test, and deploy workloads.
-
In the Google Cloud console, on the project selector page, select or create a Google Cloud project.
-
In the Google Cloud console, on the project selector page, select or create a Google Cloud project.
-
Enable the required API.
- Cloud Shell을 열려면 Google Cloud Console 툴바에서 Cloud Shell 활성화를 클릭합니다.
잠시 후 Google Cloud Console에서 Cloud Shell 세션이 열립니다.
샘플 애플리케이션
기본 목표는 서버가 처리할 수 있는 초당 쿼리 수를 최대화하는 것입니다. 부수적인 목표는 불필요한 메모리 할당을 제거하여 메모리 사용량을 줄이는 것입니다.
서버는 gRPC 프레임워크를 사용하여 단어 또는 구문을 수신한 후 셰익스피어 작품에서 해당 단어 또는 구문이 나타나는 횟수를 반환합니다.
서버가 처리할 수 있는 초당 평균 쿼리 수는 서버 부하 테스트를 통해 결정됩니다. 테스트를 실행할 때마다 클라이언트 시뮬레이터가 호출되어 20개의 순차적 쿼리를 실행하도록 지시합니다. 테스트가 한 번 완료되면 클라이언트 시뮬레이터에서 전송한 쿼리 수, 경과 시간, 초당 평균 쿼리 수가 표시됩니다.
서버 코드는 의도적으로 비효율적입니다.
샘플 애플리케이션 실행
샘플 애플리케이션을 다운로드하고 실행합니다.
Cloud Shell에서 다음 명령어를 실행합니다.
git clone https://github.com/GoogleCloudPlatform/golang-samples.git cd golang-samples/profiler/shakesapp
버전을
1
로 설정하고 테스트 횟수를 15회로 설정하여 애플리케이션을 실행합니다.go run . -version 1 -num_rounds 15
1~2분 후에 프로필 데이터가 표시됩니다. 다음 예시와 유사한 프로필 데이터가 표시됩니다.
스크린샷에서 프로필 유형이
CPU time
으로 설정되어 있음을 확인합니다. 이는 CPU 사용 데이터가 Flame 그래프에 표시됨을 나타냅니다.Cloud Shell에 다음 샘플 출력이 표시됩니다.
$ go run . -version 1 -num_rounds 15 2020/08/27 17:27:34 Simulating client requests, round 1 2020/08/27 17:27:34 Stackdriver Profiler Go Agent version: 20200618 2020/08/27 17:27:34 profiler has started 2020/08/27 17:27:34 creating a new profile via profiler service 2020/08/27 17:27:51 Simulated 20 requests in 17.3s, rate of 1.156069 reqs / sec 2020/08/27 17:27:51 Simulating client requests, round 2 2020/08/27 17:28:10 Simulated 20 requests in 19.02s, rate of 1.051525 reqs / sec 2020/08/27 17:28:10 Simulating client requests, round 3 2020/08/27 17:28:29 Simulated 20 requests in 18.71s, rate of 1.068947 reqs / sec ... 2020/08/27 17:44:32 Simulating client requests, round 14 2020/08/27 17:46:04 Simulated 20 requests in 1m32.23s, rate of 0.216849 reqs / sec 2020/08/27 17:46:04 Simulating client requests, round 15 2020/08/27 17:47:52 Simulated 20 requests in 1m48.03s, rate of 0.185134 reqs / sec
Cloud Shell 출력에는 각 반복의 경과 시간과 평균 요청 비율이 표시됩니다. 애플리케이션이 시작될 때 '17.3초에 요청 20개 시뮬레이션, 초당 요청 1.156069개'라는 항목은 서버가 초당 약 1개의 요청을 실행함을 나타냅니다. 마지막 회에서 '1분 48.03초에 요청 20개 시뮬레이션, 초당 요청 0.185134개'라는 항목은 서버가 5초마다 약 1개의 요청을 실행함을 나타냅니다.
CPU 시간 프로필을 사용하여 초당 쿼리 수 최대화
초당 쿼리 수를 최대화하는 한 가지 방법은 CPU가 많이 사용되는 메서드를 파악하고 구현을 최적화하는 것입니다. 이 섹션에서는 CPU 시간 프로필을 사용하여 서버에서 CPU를 많이 사용하는 메서드를 식별합니다.
CPU 시간 사용량 확인
Flame 그래프의 루트 프레임은 애플리케이션이 10초의 수집 간격 동안 사용한 총 CPU 시간을 나열합니다.
이 예시에서 서비스는 2.37 s
를 사용했습니다. 시스템이 단일 코어에서 실행되는 경우 CPU 시간 2.37초는 코어의 23.7% 사용에 해당합니다. 자세한 내용은 사용 가능한 프로파일링 유형을 참조하세요.
애플리케이션 수정
변경사항 평가
변경사항을 평가하려면 다음을 수행합니다.
애플리케이션 버전을
2
로 설정하여 애플리케이션을 실행합니다.go run . -version 2 -num_rounds 40
이후 섹션에서는 최적화를 통해 한 번 실행하는 데 걸리는 시간이 수정되지 않은 애플리케이션의 실행 시간보다 훨씬 짧게 나타납니다. 애플리케이션 실행 시간을 프로필을 수집하고 업로드할 수 있을 만큼 길게 늘리기 위해 횟수가 증가합니다.
애플리케이션이 완료될 때까지 기다린 후 이 애플리케이션 버전의 프로필 데이터를 봅니다.
- 지금을 클릭하여 최신 프로필 데이터를 로드합니다. 자세한 내용은 시간 범위를 참조하세요.
- 버전 메뉴에서 2를 선택합니다.
예를 들어 Flame 그래프는 다음과 같습니다.
이 그림에서 루트 프레임은 7.8 s
값을 표시합니다. 문자열 일치 함수를 변경한 결과, 애플리케이션에서 사용하는 CPU 시간이 2.37초에서 7.8초로 증가하거나 애플리케이션의 CPU 코어 사용량이 23.7%에서 78%로 변경되었습니다.
프레임 너비는 CPU 시간 사용량에 비례합니다. 이 예시에서 GetMatchCount
의 프레임 너비는 함수가 애플리케이션에서 사용한 모든 CPU 시간의 약 49%를 사용한다는 것을 나타냅니다.
원래 Flame 그래프에서 이 동일한 프레임은 그래프 너비의 약 72%를 차지했습니다.
정확한 CPU 시간 사용량을 보려면 프레임 도움말을 사용하거나 포커스 함수 목록을 사용하면 됩니다.
Cloud Shell의 출력은 수정된 버전이 초당 약 5.8개 요청을 완료함을 나타냅니다.
$ go run . -version 2 -num_rounds 40 2020/08/27 18:21:40 Simulating client requests, round 1 2020/08/27 18:21:40 Stackdriver Profiler Go Agent version: 20200618 2020/08/27 18:21:40 profiler has started 2020/08/27 18:21:40 creating a new profile via profiler service 2020/08/27 18:21:44 Simulated 20 requests in 3.67s, rate of 5.449591 reqs / sec 2020/08/27 18:21:44 Simulating client requests, round 2 2020/08/27 18:21:47 Simulated 20 requests in 3.72s, rate of 5.376344 reqs / sec 2020/08/27 18:21:47 Simulating client requests, round 3 2020/08/27 18:21:51 Simulated 20 requests in 3.58s, rate of 5.586592 reqs / sec ... 2020/08/27 18:23:51 Simulating client requests, round 39 2020/08/27 18:23:54 Simulated 20 requests in 3.46s, rate of 5.780347 reqs / sec 2020/08/27 18:23:54 Simulating client requests, round 40 2020/08/27 18:23:58 Simulated 20 requests in 3.4s, rate of 5.882353 reqs / sec
애플리케이션 이 작은 변경사항에는 두 가지 효과가 있었습니다.
초당 요청 수가 초당 1개에서 5.8개로 증가했습니다.
CPU 사용률을 초당 요청 수로 나눈 요청당 CPU 시간은 23.7%에서 13.4%로 감소했습니다.
CPU 시간 사용량이 2.37초(단일 CPU 코어 사용량 23.7%)로 증가해도 요청당 CPU 시간은 7.8초(CPU 코어의 78%)로 감소합니다.
할당된 힙 프로필을 사용하여 리소스 사용량 개선
이 섹션에서는 힙과 할당된 힙 프로필을 사용하여 애플리케이션에서 많은 할당량을 사용하는 메서드를 파악하는 방법을 설명합니다.
힙 프로필은 프로필이 수집되는 순간 프로그램의 힙에 할당된 메모리 양을 보여줍니다.
할당된 힙 프로필은 프로필이 수집된 간격에 프로그램의 힙에 할당된 총 메모리 양을 표시합니다. 이러한 값을 프로필 수집 간격인 10초로 나누어 할당 속도로 변환하면 됩니다.
힙 프로필 수집 사용 설정
애플리케이션 버전을
3
로 설정된 애플리케이션을 실행하고 힙 및 할당된 힙 프로필 모음을 사용 설정합니다.go run . -version 3 -num_rounds 40 -heap -heap_alloc
애플리케이션이 완료될 때까지 기다린 후 이 애플리케이션 버전의 프로필 데이터를 봅니다.
- 지금을 클릭하여 최신 프로필 데이터를 로드합니다.
- 버전 메뉴에서 3을 선택합니다.
- 프로파일러 유형 메뉴에서 할당된 힙을 선택합니다.
예를 들어 Flame 그래프는 다음과 같습니다.
힙 할당 속도 확인
루트 프레임은 프로필이 수집되는 10초 동안 할당된 총 힙의 양을 모든 프로필에 대한 평균으로 표시합니다. 이 예시에서 루트 프레임은 평균적으로 1.535GiB의 메모리가 할당되었음을 보여줍니다.
애플리케이션 수정
변경사항 평가
변경사항을 평가하려면 다음을 수행합니다.
애플리케이션 버전을
4
로 설정하여 애플리케이션을 실행합니다.go run . -version 4 -num_rounds 60 -heap -heap_alloc
애플리케이션이 완료될 때까지 기다린 후 이 애플리케이션 버전의 프로필 데이터를 봅니다.
- 지금을 클릭하여 최신 프로필 데이터를 로드합니다.
- 버전 메뉴에서 4를 선택합니다.
- 프로파일러 유형 메뉴에서 할당된 힙을 선택합니다.
힙 할당 속도에서
readFiles
를 변경했을 때의 효과를 수량화하기 위해 버전 4에 할당된 힙 프로필을 3에 대해 수집된 힘 프로필과 비교합니다.루트 프레임의 도움말에서는 버전 4를 사용하면 프로필 수집 중에 할당된 메모리의 평균 양이 버전 3에 비해 1.301GiB 만큼 감소했음을 보여줍니다.
readFiles.func1
의 도움말은 1.045GiB 감소를 보여줍니다.가비지 컬렉션에 미치는 영향을 수량화하려면 CPU 시간 프로필 비교를 구성합니다. 다음 스크린샷에서는 Go 가비지 수집기
runtime.gcBgMarkWorker.*
의 스택을 보여주는 필터가 적용됩니다. 스크린샷은 가비지 컬렉션의 CPU 사용량이 16.8%에서 4.97%로 감소했음을 보여줍니다.애플리케이션에서 처리하는 초당 요청 수의 변경에 따른 영향이 있는지 확인하려면 Cloud Shell에서 출력을 확인합니다. 이 예시에서 버전 4는 초당 최대 15개의 요청을 완료하며, 이는 버전 3의 초당 요청 5.8개보다 훨씬 높습니다.
$ go run . -version 4 -num_rounds 60 -heap -heap_alloc 2020/08/27 21:51:42 Simulating client requests, round 1 2020/08/27 21:51:42 Stackdriver Profiler Go Agent version: 20200618 2020/08/27 21:51:42 profiler has started 2020/08/27 21:51:42 creating a new profile via profiler service 2020/08/27 21:51:44 Simulated 20 requests in 1.47s, rate of 13.605442 reqs / sec 2020/08/27 21:51:44 Simulating client requests, round 2 2020/08/27 21:51:45 Simulated 20 requests in 1.3s, rate of 15.384615 reqs / sec 2020/08/27 21:51:45 Simulating client requests, round 3 2020/08/27 21:51:46 Simulated 20 requests in 1.31s, rate of 15.267176 reqs / sec ...
애플리케이션에서 제공하는 초당 쿼리 수가 늘어나는 것은 가비지 컬렉션 소요 시간이 줄어들기 때문일 수 있습니다.
힙 프로필을 보면
readFiles
수정의 영향을 더 잘 이해할 수 있습니다. 버전 4와 버전 3의 힙 프로필을 비교해 보면 힙 사용량이 70.95MiB에서 18.47MiB로 감소했음을 알 수 있습니다.
요약
이 빠른 시작에서는 CPU 시간 및 할당된 힙 프로필을 사용하여 애플리케이션의 잠재적 최적화를 파악했습니다. 초당 요청 수를 극대화하고 불필요한 할당을 제거하는 것이 목표였습니다.
CPU 시간 프로필을 사용하여 CPU를 많이 사용하는 함수를 식별했습니다. 간단한 변경사항을 적용한 후 서버의 요청 속도는 초당 약 1회에서 초당 5.8로 증가했습니다.
할당된 힙 프로필을 사용하여
shakesapp/server.go
함수readFiles
가 할당 비율이 높은 것으로 확인되었습니다.readFiles
를 최적화한 후 서버의 요청 속도는 초당 요청 15개로 증가했고, 10초 프로필 수집 중에 할당된 평균 메모리 양은 1.301GiB 만큼 감소했습니다.
다음 단계
Cloud Profiler 에이전트 실행 방법에 대한 자세한 내용은 다음을 참조하세요.- Go 애플리케이션 프로파일링
- 자바 애플리케이션 프로파일링
- Node.js 애플리케이션 프로파일링
- Python 애플리케이션 프로파일링
- Google Cloud 외부에서 실행되는 애플리케이션 프로파일링