Menganalisis performa produksi dengan Cloud Profiler

1. Ringkasan

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

95c034c70c9cac22.pngS

Alat ini mengumpulkan informasi penggunaan CPU dan alokasi memori dari aplikasi produksi Anda. Atribut ini mengatribusikan informasi tersebut ke kode sumber aplikasi, membantu Anda mengidentifikasi bagian aplikasi yang paling banyak menghabiskan resource, dan sebaliknya, memperjelas karakteristik performa kode. 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 Cloud Profiler untuk program Go dan memahami jenis insight tentang performa aplikasi yang dapat disajikan alat tersebut.

Yang akan Anda pelajari

  • Cara mengonfigurasi program Go untuk pembuatan profil dengan Cloud Profiler.
  • Cara mengumpulkan, melihat, dan menganalisis data performa dengan Cloud 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?

Hanya membacanya Membacanya dan menyelesaikan latihan

Bagaimana Anda menilai pengalaman Anda dengan Google Cloud Platform?

Pemula Menengah Mahir

2. Penyiapan dan Persyaratan

Penyiapan lingkungan mandiri

  1. Login ke Cloud Console lalu buat project baru atau gunakan kembali project yang sudah ada. Jika belum memiliki akun Gmail atau Google Workspace, Anda harus membuatnya.

96a9c957bc475304.png

b9a10ebdf5b5a448.png

a1e3c01a38fa61c2.png

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.

  1. Selanjutnya, Anda harus mengaktifkan penagihan di Cloud Console untuk menggunakan resource Google Cloud.

Menjalankan operasi dalam codelab ini seharusnya tidak memerlukan banyak biaya, bahkan mungkin tidak sama sekali. Pastikan untuk mengikuti petunjuk yang ada di bagian "Membersihkan" yang memberi tahu Anda cara menonaktifkan resource sehingga tidak menimbulkan penagihan di luar tutorial ini. Pengguna baru Google Cloud memenuhi syarat untuk mengikuti program Uji Coba Gratis senilai$300 USD.

Google Cloud Shell

Meskipun Google Cloud dapat dioperasikan secara jarak jauh dari laptop Anda, untuk mempermudah penyiapan dalam codelab ini, kita akan menggunakan Google Cloud Shell, yakni lingkungan command line yang berjalan di Cloud.

Mengaktifkan Cloud Shell

  1. Dari Cloud Console, klik Aktifkan Cloud Shell 4292cbf4971c9786.pngS.

bce75f34b2c53987.png

Jika belum pernah memulai Cloud Shell, Anda akan melihat layar perantara (di paruh bawah) yang menjelaskan apa itu Cloud Shell. Jika demikian, klik Lanjutkan (dan Anda tidak akan pernah melihatnya lagi). Berikut tampilan layar sekali-tampil tersebut:

70f315d7b402b476.pngS

Perlu waktu beberapa saat untuk penyediaan dan terhubung ke Cloud Shell.

fbe3a0674c982259.png

Mesin virtual ini dimuat dengan semua alat pengembangan yang Anda butuhkan. 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 dalam codelab ini dapat dilakukan hanya dengan browser atau Chromebook.

Setelah terhubung ke Cloud Shell, Anda akan melihat bahwa Anda sudah diautentikasi dan project sudah ditetapkan ke project ID Anda.

  1. Jalankan perintah berikut di Cloud Shell untuk mengonfirmasi bahwa Anda telah diautentikasi:
gcloud auth list

Output perintah

 Credentialed Accounts
ACTIVE  ACCOUNT
*       <my_account>@<my_domain.com>

To set the active account, run:
    $ gcloud config set account `ACCOUNT`
  1. Jalankan perintah berikut di Cloud Shell untuk mengonfirmasi bahwa perintah gcloud mengetahui project Anda:
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].

3. Membuka Cloud Profiler

Di Cloud Console, buka UI Profiler dengan mengklik "Profiler" di menu navigasi kiri:

37ad0df7ddb2ad17.pngS

Atau, Anda dapat menggunakan kotak penelusuran Cloud Console untuk membuka UI Profiler: cukup ketik "Cloud Profiler" dan pilih item yang ditemukan. Apa pun itu, Anda akan melihat UI Profiler dengan pesan "No data to display" seperti di bawah ini. Project ini baru, jadi belum ada data pembuatan profil yang dikumpulkan.

d275a5f61ed31fb2.png

Sekarang saatnya untuk membuat profil!

4. Membuat profil Benchmark

Kita akan menggunakan aplikasi Go sintetis sederhana yang tersedia di GitHub. Di terminal Cloud Shell yang masih Anda buka (dan saat pesan "No data to display" masih ditampilkan di UI Profiler), jalankan perintah berikut:

$ go get -u github.com/GoogleCloudPlatform/golang-samples/profiler/...

Kemudian beralihlah ke direktori aplikasi:

$ cd ~/gopath/src/github.com/GoogleCloudPlatform/golang-samples/profiler/hotapp

Direktori tersebut berisi kelas "main.go" , yaitu aplikasi sintetis yang mengaktifkan agen pembuatan profil:

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 "pertentangan").

Sekarang, jalankan program:

$ go run main.go

Saat program berjalan, agen pembuatan profil akan mengumpulkan profil dari lima jenis yang dikonfigurasi secara berkala. Pengumpulan ini diacak dari waktu ke waktu (dengan kecepatan rata-rata satu profil per menit untuk setiap jenis), sehingga mungkin perlu waktu hingga tiga menit untuk mengumpulkan setiap jenis. Program ini memberi tahu Anda saat membuat profil. Pesan diaktifkan oleh flag DebugLogging dalam konfigurasi di atas; jika tidak, agen akan berjalan tanpa suara:

$ 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 mengupdate sendiri segera setelah profil pertama dikumpulkan. Setelah itu, UI Profiler tidak akan diupdate secara otomatis. Jadi, untuk melihat data baru, Anda harus me-refresh UI Profiler secara manual. Untuk melakukannya, klik tombol Sekarang di pemilih interval waktu dua kali:

650051097b651b91.pngS

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

47a763d4dc78b6e8.pngS

Pemilih jenis profil menampilkan lima jenis profil yang tersedia:

b5d7b4b5051687c9.png

Sekarang mari kita tinjau setiap jenis profil dan beberapa kemampuan UI yang penting, lalu lakukan beberapa eksperimen. Pada tahap ini, Anda tidak lagi memerlukan terminal Cloud Shell. Anda dapat keluar dari terminal ini dengan menekan CTRL-C dan mengetik "exit".

5. Menganalisis Data Profiler

Setelah mengumpulkan beberapa data, mari kita pelajari dengan lebih cermat. Kami menggunakan aplikasi sintetis (sumbernya tersedia di GitHub) yang menyimulasikan perilaku standar dari berbagai jenis masalah performa dalam produksi.

Kode yang menggunakan CPU secara intensif

Pilih jenis profil CPU. Setelah UI memuatnya, Anda akan melihat empat blok leaf untuk fungsi load dalam grafik flame, yang secara kolektif memperhitungkan semua konsumsi CPU:

fae661c9fe6c58df.png

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 merepresentasikan biaya relatif jalur panggilan tertentu. Dalam hal ini, keempat jalur memiliki biaya yang sama. Dalam program yang sebenarnya, Anda ingin berfokus pada pengoptimalan jalur panggilan yang paling penting dalam hal performa. Grafik {i>flame<i}, yang secara visual menekankan jalur yang lebih mahal dengan kotak yang lebih besar, membuat jalur ini mudah diidentifikasi.

Anda dapat menggunakan filter data profil untuk menyaring tampilan lebih lanjut. Misalnya, coba tambahkan "Tampilkan tumpukan" filter yang menentukan "baz" sebagai string filter. Anda akan melihat sesuatu seperti screenshot di bawah, tempat hanya dua dari empat jalur panggilan ke load() yang ditampilkan. Kedua jalur ini adalah satu-satunya yang melalui fungsi dengan string "baz" dalam namanya. Pemfilteran tersebut berguna ketika Anda ingin berfokus pada subbagian program yang lebih besar (misalnya, karena Anda hanya memiliki sebagian dari program tersebut).

eb1d97491782b03f.png

Kode yang Memori Memori

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

f6311c8c841d04c4.png

Tabel ringkasan di atas grafik flame menunjukkan bahwa jumlah total memori yang digunakan di aplikasi rata-rata ~57,4 MiB, sebagian besar dialokasikan oleh fungsi allocImpl. Hal ini tidak mengejutkan, mengingat implementasi 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 ini dijalankan sekali, mengalokasikan 64 MiB dalam potongan yang lebih kecil, kemudian menyimpan pointer ke potongan tersebut dalam variabel global untuk melindunginya agar tidak dikumpulkan sampahnya. Perlu diperhatikan bahwa jumlah memori yang ditampilkan sebagai digunakan oleh profiler sedikit berbeda dari 64 MiB: profiler heap Go adalah alat statistik, sehingga pengukurannya memiliki overhead rendah tetapi tidak akurat byte. Jangan kaget ketika melihat perbedaan ~10% seperti ini.

Kode intensif IO

Jika Anda memilih "Rangkaian pesan" di pemilih jenis profil, tampilan akan beralih ke grafik flame yang sebagian besar lebarnya diambil oleh fungsi wait dan waitImpl:

ebd57fdff01dede9.png

Pada ringkasan di atas grafik flame, Anda dapat melihat bahwa ada 100 goroutine yang mengembangkan stack panggilannya dari fungsi wait. Ini benar, mengingat kode yang memulai waktu tunggu 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 waktu tunggu (seperti I/O). Stack panggilan tersebut biasanya tidak akan diambil sampelnya oleh CPU profiler, karena tidak memakan porsi besar waktu CPU. Anda mungkin sering menggunakan "Sembunyikan tumpukan" filter dengan profil Thread - misalnya, untuk menyembunyikan semua stack yang diakhiri dengan panggilan ke gopark, karena sering kali merupakan goroutine yang tidak ada aktivitas dan kurang menarik dibandingkan stack yang menunggu di I/O.

Jenis profil thread juga dapat membantu mengidentifikasi titik dalam program tempat thread menunggu mutex yang dimiliki oleh bagian lain program dalam jangka waktu yang lama, tetapi jenis profil berikut lebih berguna untuk itu.

Kode yang memerlukan banyak pertentangan

Jenis profil Pertentangan mengidentifikasi hal yang paling "diinginkan" terkunci dalam program. Jenis profil ini tersedia untuk program Go tetapi harus diaktifkan secara eksplisit dengan menetapkan "MutexProfiling: true" di kode konfigurasi agen. Pengumpulan ini bekerja dengan mencatat (di bawah metrik "Contentions") berapa kali kunci tertentu, saat dibuka oleh goroutine A, memiliki goroutine B lain yang menunggu kunci tersebut dibuka. Alat ini juga mencatat (di bawah metrik "Penundaan") waktu goroutine yang diblokir menunggu kunci terkunci. Dalam contoh ini, ada satu stack pertentangan dan total waktu tunggu untuk kunci adalah 10,5 detik:

83f00dca4a0f768e.pngS

Kode yang membuat profil ini terdiri dari 4 goroutine yang berebut 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. Ringkasan

Di lab ini, Anda telah mempelajari cara mengonfigurasi program Go untuk digunakan dengan Cloud Profiler. Anda juga mempelajari cara mengumpulkan, melihat, dan menganalisis data performa dengan alat ini. Sekarang Anda dapat menerapkan keterampilan baru Anda ke layanan nyata yang Anda jalankan di Google Cloud Platform.

7. Selamat!

Anda telah mempelajari cara mengonfigurasi dan menggunakan Cloud Profiler.

Pelajari Lebih Lanjut

Lisensi

Karya ini dilisensikan berdasarkan Lisensi Umum Creative Commons Attribution 2.0.