1. Tổng quan
Mặc dù các nhà phát triển ứng dụng khách và web giao diện người dùng thường sử dụng các công cụ như Trình phân tích CPU của Android Studio hoặc các công cụ phân tích tài nguyên có trong Chrome để cải thiện hiệu suất của mã, nhưng những kỹ thuật tương đương vẫn chưa được những người làm việc trên các dịch vụ phụ trợ có thể tiếp cận hoặc áp dụng một cách hiệu quả. Trình phân tích tài nguyên trên đám mây cũng mang đến những chức năng tương tự cho các nhà phát triển dịch vụ, bất kể mã của họ đang chạy trên Google Cloud Platform hay ở nơi khác.
Công cụ này thu thập thông tin về mức sử dụng CPU và mức phân bổ bộ nhớ từ các ứng dụng phát hành chính thức. Lớp này phân bổ thông tin đó cho mã nguồn của ứng dụng, giúp bạn xác định các phần của ứng dụng tiêu tốn nhiều tài nguyên nhất và làm sáng tỏ các đặc điểm về hiệu suất của mã. Mức hao tổn thấp của các kỹ thuật thu thập mà công cụ này triển khai giúp công cụ này phù hợp để sử dụng liên tục trong môi trường sản xuất.
Trong lớp học lập trình này, bạn sẽ tìm hiểu cách thiết lập Trình phân tích tài nguyên trên đám mây cho chương trình Go và sẽ làm quen với những loại thông tin chi tiết về hiệu suất của ứng dụng mà công cụ này có thể cung cấp.
Kiến thức bạn sẽ học được
- Cách định cấu hình chương trình Go để phân tích bằng Trình phân tích đám mây.
- Cách thu thập, xem và phân tích dữ liệu hiệu suất bằng Trình phân tích tài nguyên trên đám mây.
Bạn cần có
- Một dự án Google Cloud Platform
- Một trình duyệt, chẳng hạn như Chrome hoặc Firefox
- Quen thuộc với các trình soạn thảo văn bản tiêu chuẩn của Linux như Vim, EMAC hoặc Nano
Bạn sẽ dùng hướng dẫn này như thế nào?
Bạn đánh giá thế nào về trải nghiệm sử dụng Google Cloud Platform?
2. Thiết lập và yêu cầu
Thiết lập môi trường theo tiến độ riêng
- Đăng nhập vào Cloud Console rồi tạo dự án mới hoặc sử dụng lại dự án hiện có. Nếu chưa có tài khoản Gmail hoặc Google Workspace, bạn phải tạo một tài khoản.
Xin lưu ý rằng mã dự án là một tên riêng biệt trong tất cả dự án Google Cloud (tên ở trên đã được sử dụng nên sẽ không phù hợp với bạn!). Lớp này sẽ được đề cập sau trong lớp học lập trình này là PROJECT_ID
.
- Tiếp theo, bạn sẽ cần bật tính năng thanh toán trong Cloud Console để sử dụng tài nguyên của Google Cloud.
Việc chạy qua lớp học lập trình này sẽ không tốn nhiều chi phí. Hãy nhớ làm theo mọi hướng dẫn trong phần "Dọn dẹp" sẽ tư vấn cho bạn cách tắt tài nguyên để bạn không phải chịu thanh toán ngoài hướng dẫn này. Người dùng mới của Google Cloud đủ điều kiện tham gia chương trình Dùng thử miễn phí 300 USD.
Google Cloud Shell
Mặc dù bạn có thể vận hành Google Cloud từ xa trên máy tính xách tay, nhưng để đơn giản hoá việc thiết lập trong lớp học lập trình này, chúng ta sẽ sử dụng Google Cloud Shell, một môi trường dòng lệnh chạy trên Đám mây.
Kích hoạt Cloud Shell
- Trong Cloud Console, hãy nhấp vào Kích hoạt Cloud Shell .
Nếu trước đây bạn chưa từng khởi động Cloud Shell, thì bạn sẽ thấy một màn hình trung gian (dưới màn hình đầu tiên) mô tả về ứng dụng này. Nếu trường hợp đó xảy ra, hãy nhấp vào Tiếp tục (và bạn sẽ không thấy thông báo đó nữa). Màn hình một lần đó sẽ có dạng như sau:
Quá trình cấp phép và kết nối với Cloud Shell chỉ mất vài phút.
Máy ảo này chứa tất cả các công cụ phát triển mà bạn cần. Dịch vụ này cung cấp thư mục gốc có dung lượng ổn định 5 GB và chạy trong Google Cloud, giúp nâng cao đáng kể hiệu suất và khả năng xác thực của mạng. Trong lớp học lập trình này, đa số mọi người đều có thể thực hiện chỉ bằng một trình duyệt hoặc Chromebook.
Sau khi kết nối với Cloud Shell, bạn sẽ thấy mình đã được xác thực và dự án đã được đặt thành mã dự án.
- Chạy lệnh sau trong Cloud Shell để xác nhận rằng bạn đã được xác thực:
gcloud auth list
Kết quả lệnh
Credentialed Accounts ACTIVE ACCOUNT * <my_account>@<my_domain.com> To set the active account, run: $ gcloud config set account `ACCOUNT`
- Chạy lệnh sau trong Cloud Shell để xác nhận rằng lệnh gcloud biết về dự án của bạn:
gcloud config list project
Kết quả lệnh
[core] project = <PROJECT_ID>
Nếu chưa, bạn có thể đặt chế độ này bằng lệnh sau:
gcloud config set project <PROJECT_ID>
Kết quả lệnh
Updated property [core/project].
3. Chuyển đến Trình phân tích tài nguyên trên đám mây
Trong Cloud Console, hãy chuyển đến giao diện người dùng của Trình phân tích tài nguyên bằng cách nhấp vào "Trình phân tích tài nguyên" trong thanh điều hướng bên trái:
Ngoài ra, bạn có thể dùng thanh tìm kiếm trên Cloud Console để chuyển đến giao diện người dùng của Trình phân tích tài nguyên: chỉ cần nhập "Cloud Profiler" rồi chọn nội dung tìm được. Dù bằng cách nào, bạn cũng sẽ thấy giao diện người dùng của Trình phân tích tài nguyên với thông báo "Không có dữ liệu để hiển thị" như bên dưới. Dự án này mới nên chưa có dữ liệu lập hồ sơ nào được thu thập.
Đã đến lúc lập hồ sơ cho tài liệu nào đó!
4. Phân tích điểm chuẩn
Chúng tôi sẽ sử dụng một ứng dụng Go tổng hợp đơn giản có trên GitHub. Trong cửa sổ dòng lệnh Cloud Shell mà bạn vẫn đang mở (và trong khi thông báo "Không có dữ liệu để hiển thị" vẫn hiển thị trong giao diện người dùng của Trình phân tích tài nguyên, hãy chạy lệnh sau:
$ go get -u github.com/GoogleCloudPlatform/golang-samples/profiler/...
Sau đó, hãy chuyển sang thư mục ứng dụng:
$ cd ~/gopath/src/github.com/GoogleCloudPlatform/golang-samples/profiler/hotapp
Thư mục này có chứa "main.go" một ứng dụng tổng hợp đã bật tác nhân phân tích tài nguyên:
main.go
...
import (
...
"cloud.google.com/go/profiler"
)
...
func main() {
err := profiler.Start(profiler.Config{
Service: "hotapp-service",
DebugLogging: true,
MutexProfiling: true,
})
if err != nil {
log.Fatalf("failed to start the profiler: %v", err)
}
...
}
Theo mặc định, tác nhân phân tích tài nguyên sẽ thu thập hồ sơ CPU, vùng nhớ khối xếp và luồng. Mã ở đây cho phép tập hợp các cấu hình mutex (còn được gọi là "tranh chấp").
Bây giờ, hãy chạy chương trình:
$ go run main.go
Khi chương trình chạy, tác nhân phân tích tài nguyên sẽ thu thập định kỳ hồ sơ của 5 loại đã định cấu hình. Dữ liệu thu thập được chọn ngẫu nhiên theo thời gian (với tốc độ trung bình là một hồ sơ/phút cho mỗi loại). Do đó, có thể mất tối đa 3 phút để thu thập từng loại hồ sơ. Chương trình này sẽ cho bạn biết khi nào nó tạo hồ sơ. Các thông báo được bật bằng cờ DebugLogging
trong cấu hình ở trên; nếu không, tác nhân sẽ tự chạy:
$ go run main.go 2018/03/28 15:10:24 profiler has started 2018/03/28 15:10:57 successfully created profile THREADS 2018/03/28 15:10:57 start uploading profile 2018/03/28 15:11:19 successfully created profile CONTENTION 2018/03/28 15:11:30 start uploading profile 2018/03/28 15:11:40 successfully created profile CPU 2018/03/28 15:11:51 start uploading profile 2018/03/28 15:11:53 successfully created profile CONTENTION 2018/03/28 15:12:03 start uploading profile 2018/03/28 15:12:04 successfully created profile HEAP 2018/03/28 15:12:04 start uploading profile 2018/03/28 15:12:04 successfully created profile THREADS 2018/03/28 15:12:04 start uploading profile 2018/03/28 15:12:25 successfully created profile HEAP 2018/03/28 15:12:25 start uploading profile 2018/03/28 15:12:37 successfully created profile CPU ...
Giao diện người dùng sẽ tự cập nhật ngay sau khi hồ sơ được thu thập. Trình phân tích tài nguyên sẽ không tự động cập nhật sau đó, vì vậy, để xem dữ liệu mới, bạn cần làm mới giao diện người dùng của Trình phân tích tài nguyên theo cách thủ công. Để làm việc đó, hãy nhấp hai lần vào nút Hiện hành trong bộ chọn khoảng thời gian:
Sau khi giao diện người dùng được làm mới, bạn sẽ thấy giao diện như sau:
Bộ chọn loại hồ sơ cho thấy 5 loại hồ sơ có sẵn:
Bây giờ, chúng ta hãy xem xét từng loại hồ sơ và một số chức năng quan trọng của giao diện người dùng, sau đó tiến hành một số thử nghiệm. Ở giai đoạn này, bạn không cần thiết bị đầu cuối Cloud Shell nữa, vì vậy, bạn có thể thoát bằng cách nhấn Ctrl-C rồi nhập "exit".
5. Phân tích dữ liệu trình phân tích tài nguyên
Sau khi chúng ta đã thu thập một số dữ liệu, hãy cùng xem xét kỹ hơn. Chúng tôi đang sử dụng một ứng dụng tổng hợp (nguồn có trên GitHub) mô phỏng các hành vi điển hình cho nhiều loại vấn đề về hiệu suất trong phiên bản chính thức.
Mã dùng nhiều CPU
Chọn loại hồ sơ CPU. Sau khi giao diện người dùng tải hàm này, bạn sẽ thấy trong biểu đồ hình ngọn lửa, bạn sẽ thấy 4 khối lá cho hàm load
, tính chung cho tất cả mức tiêu thụ CPU:
Hàm này được viết riêng để tiêu thụ nhiều chu kỳ CPU bằng cách chạy một vòng lặp chặt chẽ:
main.go
func load() {
for i := 0; i < (1 << 20); i++ {
}
}
Hàm này được gọi gián tiếp từ busyloop
() qua 4 đường dẫn lệnh gọi: busyloop
→ {foo1
, foo2
} → {bar
, baz
} → load
. Chiều rộng của một hộp hàm biểu thị chi phí tương đối của một đường dẫn lệnh gọi cụ thể. Trong trường hợp này, cả bốn đường dẫn đều có cùng chi phí. Trong một chương trình thực tế, bạn sẽ muốn tập trung vào việc tối ưu hoá các đường dẫn cuộc gọi quan trọng nhất về hiệu suất. Biểu đồ hình ngọn lửa làm nổi bật các đường dẫn đắt tiền hơn với các hộp lớn hơn một cách trực quan giúp dễ dàng xác định những đường dẫn này.
Bạn có thể sử dụng bộ lọc dữ liệu tiểu sử để tinh chỉnh thêm hiển thị. Ví dụ: thử thêm yêu cầu "Hiển thị ngăn xếp" bộ lọc chỉ định "baz" làm chuỗi bộ lọc. Bạn sẽ thấy nội dung tương tự như ảnh chụp màn hình bên dưới, trong đó chỉ có 2 trong số 4 đường dẫn gọi đến load()
được hiển thị. Hai đường dẫn này là đường dẫn duy nhất đi qua một hàm có chuỗi "baz" trong tên. Cách lọc như vậy sẽ hữu ích khi bạn muốn tập trung vào một phần con của chương trình lớn hơn (ví dụ: vì bạn chỉ sở hữu một phần của chương trình đó).
Mã tốn nhiều bộ nhớ
Bây giờ, hãy chuyển sang "Vùng nhớ khối xếp" hồ sơ. Hãy nhớ xóa mọi bộ lọc mà bạn đã tạo trong các thử nghiệm trước đó. Bây giờ, bạn sẽ thấy một biểu đồ hình ngọn lửa trong đó allocImpl
, được gọi bởi alloc
, được hiển thị dưới dạng đối tượng sử dụng bộ nhớ chính trong ứng dụng:
Bảng tóm tắt phía trên biểu đồ hình ngọn lửa cho biết tổng dung lượng bộ nhớ đã dùng trong ứng dụng trung bình là ~57,4 MiB, phần lớn bộ nhớ này được phân bổ theo hàm allocImpl
. Điều này không có gì đáng ngạc nhiên, vì việc triển khai hàm này:
main.go
func allocImpl() {
// Allocate 64 MiB in 64 KiB chunks
for i := 0; i < 64*16; i++ {
mem = append(mem, make([]byte, 64*1024))
}
}
Hàm này thực thi một lần, phân bổ 64 MiB trong các đoạn nhỏ hơn, sau đó lưu trữ con trỏ vào những đoạn đó trong một biến toàn cục để chúng không bị thu gom rác. Lưu ý rằng dung lượng bộ nhớ được trình phân tích sử dụng hơi khác so với 64 MiB: Trình phân tích vùng nhớ khối xếp Go là một công cụ thống kê, do đó, các phép đo có mức hao tổn thấp nhưng không chính xác về byte. Đừng ngạc nhiên khi thấy mức chênh lệch khoảng 10% như thế này.
Mã chuyên sâu IO
Nếu bạn chọn "Chuỗi" trong bộ chọn loại hồ sơ, màn hình sẽ chuyển sang biểu đồ hình ngọn lửa, trong đó phần lớn chiều rộng được lấy bởi các hàm wait
và waitImpl
:
Trong bản tóm tắt biểu đồ hình ngọn lửa ở trên, bạn có thể thấy có 100 con khỉ đột phát triển ngăn xếp lệnh gọi từ hàm wait
. Điều này hoàn toàn đúng, vì mã bắt đầu các lần chờ này có dạng như sau:
main.go
func main() {
...
// Simulate some waiting goroutines.
for i := 0; i < 100; i++ {
go wait()
}
Loại hồ sơ này rất hữu ích trong việc tìm hiểu xem chương trình có phải chờ đợi ngoài dự kiến hay không (như I/O). Các ngăn xếp lệnh gọi như vậy thường sẽ không được trình phân tích CPU lấy mẫu vì chúng không chiếm phần đáng kể thời gian của CPU. Bạn thường muốn sử dụng tính năng "Ẩn ngăn xếp" bộ lọc có cấu hình Threads (luồng) – ví dụ: để ẩn tất cả các ngăn xếp kết thúc bằng lệnh gọi đến gopark,
vì chúng thường là những con khỉ đột ở trạng thái rảnh và ít thú vị hơn những ngăn xếp chờ I/O.
Loại hồ sơ luồng cũng có thể giúp xác định các điểm trong chương trình mà trong đó các luồng đang chờ một mutex thuộc sở hữu của một phần khác của chương trình trong một thời gian dài, nhưng loại hồ sơ sau đây sẽ hữu ích hơn cho điều đó.
Đoạn mã chuyên sâu về tranh chấp
Loại hồ sơ Nội dung xác định nội dung "muốn" nhất khoá trong chương trình. Loại hồ sơ này có sẵn cho các chương trình Go nhưng bạn phải cho phép rõ ràng bằng cách chỉ định "MutexProfiling: true
" trong mã cấu hình của tác nhân. Bộ sưu tập hoạt động bằng cách ghi lại (theo chỉ số "Tranh chấp") số lần một khoá cụ thể (khi được mở khoá bởi một con khỉ đột A) có một con khỉ đột B khác đang chờ khoá được mở khoá. Mã này cũng ghi lại (trong chỉ số "Độ trễ") thời gian mà khỉ đột bị chặn chờ khoá. Trong ví dụ này, có một ngăn xếp tranh chấp duy nhất và tổng thời gian chờ khoá là 10,5 giây:
Đoạn mã tạo hồ sơ này bao gồm 4 con khỉ đột chiến đấu với một mutex:
main.go
func contention(d time.Duration) {
contentionImpl(d)
}
func contentionImpl(d time.Duration) {
for {
mu.Lock()
time.Sleep(d)
mu.Unlock()
}
}
...
func main() {
...
for i := 0; i < 4; i++ {
go contention(time.Duration(i) * 50 * time.Millisecond)
}
}
6. Tóm tắt
Trong phòng thí nghiệm này, bạn đã tìm hiểu cách định cấu hình một chương trình Go để sử dụng với Cloud Profiler. Bạn cũng đã tìm hiểu cách thu thập, xem và phân tích dữ liệu hiệu suất bằng công cụ này. Giờ đây, bạn có thể áp dụng kỹ năng mới của mình vào các dịch vụ thực mà bạn chạy trên Google Cloud Platform.
7. Xin chúc mừng!
Bạn đã tìm hiểu cách định cấu hình và sử dụng Cloud Profiler!
Tìm hiểu thêm
- Trình phân tích tài nguyên trên đám mây: https://cloud.google.com/profiler/
- Chuyển đến gói thời gian chạy/pprof mà Trình phân tích tài nguyên đám mây sử dụng: https://golang.org/pkg/runtime/pprof/
Giấy phép
Tác phẩm này được cấp phép theo Giấy phép chung Ghi nhận tác giả Creative Commons 2.0.