Meskipun developer aplikasi klien dan web frontend biasanya menggunakan alat seperti CPU Profiler Android Studio atau alat pembuatan profil yang disertakan dalam Chrome untuk meningkatkan performa kode mereka, teknik yang setara belum dapat diakses atau diadopsi dengan baik oleh mereka yang mengerjakan layanan backend. Stackdriver Profiler menghadirkan kemampuan yang sama ini kepada developer layanan, terlepas dari apakah kode mereka berjalan di Google Cloud Platform atau di tempat lain.

Alat ini mengumpulkan informasi penggunaan CPU dan alokasi memori dari aplikasi produksi Anda. Cloud Profiler mengatribusikan informasi tersebut ke kode sumber aplikasi, membantu Anda mengidentifikasi bagian aplikasi yang paling banyak menggunakan resource, dan menunjukkan karakteristik performa kode tersebut. Overhead rendah dari teknik pengumpulan yang digunakan oleh alat ini membuatnya cocok untuk penggunaan berkelanjutan di lingkungan produksi.
Dalam codelab ini, Anda akan mempelajari cara menyiapkan Stackdriver Profiler untuk program Go dan akan memahami jenis insight tentang performa aplikasi yang dapat ditampilkan oleh alat ini.
Yang akan Anda pelajari
- Cara mengonfigurasi program Go untuk pembuatan profil dengan Stackdriver Profiler.
- Cara mengumpulkan, melihat, dan menganalisis data performa dengan Stackdriver Profiler.
Yang Anda butuhkan
- Project Google Cloud Platform
- Browser, seperti Chrome atau Firefox
- Pemahaman tentang editor teks Linux standar seperti Vim, EMACs, atau Nano
Bagaimana Anda akan menggunakan tutorial ini?
Bagaimana penilaian Anda terhadap pengalaman menggunakan Google Cloud Platform?
Penyiapan lingkungan mandiri
Jika belum memiliki Akun Google (Gmail atau Google Apps), Anda harus membuatnya. Login ke Google Cloud Platform console (console.cloud.google.com) dan buat project baru:
Ingat project ID, nama unik di semua project Google Cloud (maaf, nama di atas telah digunakan dan tidak akan berfungsi untuk Anda!) Project ID tersebut selanjutnya akan dirujuk di codelab ini sebagai PROJECT_ID.
Selanjutnya, Anda harus mengaktifkan penagihan di Konsol Cloud untuk menggunakan resource Google Cloud.
Menjalankan melalui codelab ini tidak akan menghabiskan biaya lebih dari beberapa dolar, tetapi bisa lebih jika Anda memutuskan untuk menggunakan lebih banyak resource atau jika Anda membiarkannya berjalan (lihat bagian "pembersihan" di akhir dokumen ini).
Pengguna baru Google Cloud Platform memenuhi syarat untuk mendapatkan uji coba gratis senilai$300.
Google Cloud Shell
Meskipun Google Cloud dapat dioperasikan dari jarak jauh menggunakan laptop Anda, untuk menyederhanakan penyiapan dalam codelab ini, kita akan menggunakan Google Cloud Shell, lingkungan command line yang berjalan di Cloud.
Mengaktifkan Google Cloud Shell
Dari Konsol GCP, klik ikon Cloud Shell di toolbar kanan atas:
Kemudian, klik "Start Cloud Shell":
Hanya perlu waktu beberapa saat untuk penyediaan dan terhubung ke lingkungan:
Mesin virtual ini berisi semua alat pengembangan yang Anda perlukan. Layanan ini menawarkan direktori beranda tetap sebesar 5 GB dan beroperasi di Google Cloud, sehingga sangat meningkatkan performa dan autentikasi jaringan. Sebagian besar pekerjaan Anda di lab ini dapat dilakukan hanya dengan browser atau Google Chromebook.
Setelah terhubung ke Cloud Shell, Anda akan melihat bahwa Anda sudah diautentikasi dan project sudah ditetapkan ke PROJECT_ID Anda.
Jalankan perintah berikut di Cloud Shell untuk mengonfirmasi bahwa Anda telah diautentikasi:
gcloud auth list
Output perintah
Credentialed accounts: - <myaccount>@<mydomain>.com (active)
gcloud config list project
Output perintah
[core] project = <PROJECT_ID>
Jika tidak, Anda dapat menyetelnya dengan perintah ini:
gcloud config set project <PROJECT_ID>
Output perintah
Updated property [core/project].
Di Konsol Cloud, buka UI Profiler dengan mengklik "Profiler" di menu navigasi sebelah kiri:

Atau, Anda dapat menggunakan kotak penelusuran Konsol Cloud untuk membuka UI Profiler: cukup ketik "Stackdriver Profiler" dan pilih item yang ditemukan. Bagaimanapun, Anda akan melihat UI Profiler dengan pesan "Tidak ada data untuk ditampilkan" seperti di bawah. Project ini baru, sehingga belum mengumpulkan data pembuatan profil.

Sekarang saatnya memprofilkan sesuatu.
Kita akan menggunakan aplikasi Go sintetis sederhana yang tersedia di GitHub. Di terminal Cloud Shell yang masih Anda buka (dan saat pesan "Tidak ada data untuk ditampilkan" masih ditampilkan di UI Profiler), jalankan perintah berikut:
$ go get -u github.com/GoogleCloudPlatform/golang-samples/profiler/...
Kemudian, beralih ke direktori aplikasi:
$ cd ~/gopath/src/github.com/GoogleCloudPlatform/golang-samples/profiler/hotapp
Direktori ini berisi file "main.go", yang merupakan aplikasi sintetis dengan agen pembuatan profil yang diaktifkan:
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)
}
...
}Agen pembuatan profil mengumpulkan profil CPU, heap, dan thread secara default. Kode di sini memungkinkan pengumpulan profil mutex (juga dikenal sebagai "persaingan").
Sekarang, jalankan program:
$ go run main.go
Saat program berjalan, agen pembuatan profil akan mengumpulkan profil dari lima jenis yang dikonfigurasi secara berkala. Pengumpulan data diacak dari waktu ke waktu (dengan kecepatan rata-rata satu profil per menit untuk setiap jenis), sehingga mungkin diperlukan waktu hingga tiga menit untuk mengumpulkan setiap jenis. Program akan memberi tahu Anda saat membuat profil. Pesan diaktifkan oleh tanda DebugLogging dalam konfigurasi di atas; jika tidak, agen akan berjalan tanpa pemberitahuan:
$ 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 ...
UI akan diperbarui sendiri segera setelah profil pertama dikumpulkan. Setelah itu, Profiler tidak akan otomatis diperbarui, jadi untuk melihat data baru, Anda harus memperbarui UI Profiler secara manual. Untuk melakukannya, klik tombol Sekarang di pemilih interval waktu dua kali:

Setelah UI dimuat ulang, Anda akan melihat tampilan seperti ini:

Pemilih jenis profil menampilkan lima jenis profil yang tersedia:

Sekarang, mari kita tinjau setiap jenis profil dan beberapa kemampuan UI penting, lalu lakukan beberapa eksperimen. Pada tahap ini, Anda tidak lagi memerlukan terminal Cloud Shell, jadi Anda dapat keluar dengan menekan CTRL-C dan mengetik "exit".
Setelah mengumpulkan beberapa data, mari kita lihat lebih dekat. Kami menggunakan aplikasi sintetis (sumbernya tersedia di Github) yang menyimulasikan perilaku yang biasanya terjadi pada berbagai jenis masalah performa dalam produksi.
Kode yang Intensif CPU
Pilih jenis profil CPU. Setelah UI memuatnya, Anda akan melihat empat blok daun untuk fungsi load dalam grafik flame, yang secara kolektif mencakup semua penggunaan CPU:

Fungsi ini ditulis secara khusus untuk menggunakan banyak siklus CPU dengan menjalankan loop yang ketat:
main.go
func load() {
for i := 0; i < (1 << 20); i++ {
}
}Fungsi ini dipanggil secara tidak langsung dari busyloop() melalui empat jalur panggilan: busyloop → {foo1, foo2} → {bar, baz} → load. Lebar kotak fungsi menunjukkan biaya relatif dari jalur panggilan tertentu. Dalam hal ini, keempat jalur memiliki biaya yang hampir sama. Dalam program yang sebenarnya, Anda harus berfokus pada pengoptimalan jalur panggilan yang paling penting dalam hal performa. Grafik flame, yang secara visual menekankan jalur yang lebih mahal dengan kotak yang lebih besar, memudahkan identifikasi jalur ini.
Anda dapat menggunakan filter data profil untuk menyaring tampilan lebih lanjut. Misalnya, coba tambahkan filter "Tampilkan stack" yang menentukan "baz" sebagai string filter. Anda akan melihat sesuatu seperti screenshot di bawah, di mana hanya dua dari empat jalur panggilan ke load() yang ditampilkan. Kedua jalur ini adalah satu-satunya jalur yang melewati fungsi dengan string "baz" di namanya. Pemfilteran ini berguna jika Anda ingin berfokus pada subbagian dari program yang lebih besar (misalnya, karena Anda hanya memiliki sebagian program tersebut).

Kode yang Membutuhkan Banyak Memori
Sekarang beralihlah ke jenis profil "Heap". Pastikan untuk menghapus filter yang Anda buat dalam eksperimen sebelumnya. Sekarang Anda akan melihat grafik flame tempat allocImpl, yang dipanggil oleh alloc, ditampilkan sebagai konsumen utama memori di aplikasi:

Tabel ringkasan di atas grafik flame menunjukkan bahwa jumlah total memori yang digunakan dalam aplikasi rata-rata ~57,4 MiB, sebagian besar dialokasikan oleh fungsi allocImpl. Hal ini tidak mengejutkan, mengingat penerapan fungsi ini:
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))
}
}Fungsi dijalankan sekali, mengalokasikan 64 MiB dalam potongan yang lebih kecil, lalu menyimpan pointer ke potongan tersebut dalam variabel global untuk melindunginya dari pengumpulan sampah. Perhatikan bahwa jumlah memori yang ditampilkan sebagai yang digunakan oleh profiler sedikit berbeda dari 64 MiB: profiler heap Go adalah alat statistik, sehingga pengukuran memiliki overhead rendah, tetapi tidak akurat per byte. Jangan heran jika melihat perbedaan sekitar 10% seperti ini.
Kode yang Intensif IO
Jika Anda memilih "Threads" di pemilih jenis profil, tampilan akan beralih ke grafik api yang sebagian besar lebarnya digunakan oleh fungsi wait dan waitImpl:

Dalam ringkasan di atas grafik flame, Anda dapat melihat bahwa ada 100 goroutine yang memperbesar stack panggilannya dari fungsi wait. Ini sudah benar, mengingat kode yang memulai penantian ini terlihat seperti ini:
main.go
func main() {
...
// Simulate some waiting goroutines.
for i := 0; i < 100; i++ {
go wait()
}Jenis profil ini berguna untuk memahami apakah program menghabiskan waktu yang tidak terduga dalam menunggu (seperti I/O). Stack panggilan tersebut biasanya tidak diambil sampelnya oleh profiler CPU, karena tidak menggunakan sebagian besar waktu CPU. Anda sering kali ingin menggunakan filter "Sembunyikan stack" dengan profil Thread - misalnya, untuk menyembunyikan semua stack yang diakhiri dengan panggilan ke gopark, karena stack tersebut sering kali merupakan goroutine yang tidak aktif dan kurang menarik daripada stack yang menunggu I/O.
Jenis profil thread juga dapat membantu mengidentifikasi titik dalam program tempat thread menunggu mutex yang dimiliki oleh bagian lain dari program dalam jangka waktu yang lama, tetapi jenis profil berikut lebih berguna untuk hal tersebut.
Kode dengan Persaingan Tinggi
Jenis profil Persaingan mengidentifikasi kunci yang paling "diinginkan" dalam program. Jenis profil ini tersedia untuk program Go, tetapi harus diaktifkan secara eksplisit dengan menentukan "MutexProfiling: true" dalam kode konfigurasi agen. Pengumpulan data ini berfungsi dengan mencatat (di bawah metrik "Contentions") jumlah waktu saat kunci tertentu, saat dibuka oleh goroutine A, membuat goroutine B lain menunggu kunci dibuka. Kode ini juga mencatat (di bawah metrik "Penundaan") waktu tunggu goroutine yang diblokir untuk kunci. Dalam contoh ini, ada satu stack pertentangan dan total waktu tunggu untuk kunci adalah 11,03 detik:

Kode yang menghasilkan profil ini terdiri dari 4 goroutine yang bersaing untuk mendapatkan 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)
}
}Di lab ini, Anda telah mempelajari cara mengonfigurasi program Go untuk digunakan dengan Stackdriver Profiler. Anda juga mempelajari cara mengumpulkan, melihat, dan menganalisis data performa dengan alat ini. Sekarang Anda dapat menerapkan keterampilan baru Anda ke layanan sebenarnya yang Anda jalankan di Google Cloud Platform.
Anda telah mempelajari cara mengonfigurasi dan menggunakan Stackdriver Profiler.
Pelajari Lebih Lanjut
- Stackdriver Profiler: https://cloud.google.com/profiler/
- Paket pprof/runtime Go yang digunakan Stackdriver Profiler: https://golang.org/pkg/runtime/pprof/
Lisensi
Karya ini dilisensikan berdasarkan Lisensi Umum Creative Commons Attribution 2.0.