แม้ว่าโดยทั่วไปแล้วแอปไคลเอ็นต์และนักพัฒนาเว็บฟรอนท์เอนด์จะใช้งานเครื่องมืออย่าง Android Studio CPU Profiler หรือเครื่องมือสร้างโปรไฟล์ที่รวมอยู่ใน Chrome เพื่อปรับปรุงประสิทธิภาพของโค้ดได้ แต่เทคนิคที่เทียบเท่ากันนั้นมักไม่ค่อยได้ผลหรือมีไว้สําหรับผู้ที่ใช้งานบริการแบ็กเอนด์เท่านั้น Stackdriver Profiler มอบความสามารถเดียวกันนี้ให้แก่นักพัฒนาแอปไม่ว่าโค้ดจะทํางานบน Google Cloud Platform หรือที่อื่นๆ
เครื่องมือจะรวบรวมข้อมูลการใช้งาน CPU และการจัดสรรหน่วยความจําจากแอปพลิเคชันที่ใช้งานจริง แอตทริบิวต์ดังกล่าวระบุข้อมูลไปยังซอร์สโค้ดของแอปพลิเคชัน ซึ่งจะช่วยระบุส่วนต่างๆ ของแอปพลิเคชันที่ใช้ทรัพยากรมากที่สุด รวมถึงแสดงลักษณะเฉพาะของโค้ด โอเวอร์เฮดของเทคนิคการรวบรวมที่ใช้กับเครื่องมือดังกล่าวทําให้เหมาะกับการใช้งานในสภาพแวดล้อมการใช้งานอย่างต่อเนื่อง
ใน Codelab นี้ คุณจะได้เรียนรู้วิธีตั้งค่า Stackdriver Profiler สําหรับโปรแกรม Go แล้วทําความคุ้นเคยกับประเภทข้อมูลเชิงลึกเกี่ยวกับประสิทธิภาพของแอปพลิเคชันที่เครื่องมือแสดงได้
สิ่งที่จะได้เรียนรู้
- วิธีกําหนดค่าโปรแกรม Go สําหรับการทําโปรไฟล์ด้วย Stackdriver Profiler
- วิธีรวบรวม ดู และวิเคราะห์ข้อมูลประสิทธิภาพด้วย Stackdriver Profiler
สิ่งที่ต้องมี
- โครงการ Google Cloud Platform
- เบราว์เซอร์ เช่น Chrome หรือ Firefox
- ทําความคุ้นเคยกับเครื่องมือแก้ไขข้อความ Linux มาตรฐาน เช่น Vim, EMAC หรือ Nano
คุณจะใช้บทแนะนํานี้อย่างไร
คุณจะให้คะแนนประสบการณ์การใช้งาน Google Cloud Platform อย่างไร
การตั้งค่าสภาพแวดล้อมด้วยตนเอง
หากยังไม่มีบัญชี Google (Gmail หรือ Google Apps) คุณต้องสร้างบัญชี ลงชื่อเข้าใช้คอนโซล Google Cloud Platform (console.cloud.google.com) และสร้างโปรเจ็กต์ใหม่ ดังนี้
โปรดทราบว่ารหัสโปรเจ็กต์ ซึ่งเป็นชื่อที่ไม่ซ้ํากันสําหรับโปรเจ็กต์ Google Cloud ทั้งหมด (ชื่อข้างต้นมีผู้อื่นนําไปใช้แล้ว ขออภัยในความไม่สะดวก) และจะเรียกใน Codelab นี้ว่า PROJECT_ID
ในภายหลัง
จากนั้นคุณจะต้องเปิดใช้การเรียกเก็บเงินใน Cloud Console เพื่อใช้ทรัพยากรของ Google Cloud
การเรียกใช้ Codelab นี้ไม่ควรมีค่าใช้จ่ายเกิน 2-3 ดอลลาร์ แต่อาจมากกว่านั้นหากคุณตัดสินใจใช้ทรัพยากรเพิ่มเติมหรือปล่อยให้ทรัพยากรทํางาน (ดู "cleanup" ในตอนท้ายของเอกสารนี้)
ผู้ใช้ใหม่ของ Google Cloud Platform มีสิทธิ์รับช่วงทดลองใช้ฟรี$300
Google Cloud Shell
แม้ว่า Google Cloud จะทํางานจากแล็ปท็อปได้จากระยะไกล แต่เพื่อให้การตั้งค่าใน Codelab นี้ง่ายขึ้น เราจะใช้ Google Cloud Shell ซึ่งเป็นสภาพแวดล้อมบรรทัดคําสั่งที่ทํางานในระบบคลาวด์
เปิดใช้งาน Google Cloud Shell
จากคอนโซล GCP ให้คลิกไอคอน Cloud Shell บนแถบเครื่องมือด้านขวาบน ดังนี้
จากนั้นคลิก "เริ่ม Cloud Shell"
ระบบจะใช้เวลาเพียงครู่เดียวในการจัดสรรและเชื่อมต่อกับสภาพแวดล้อม
เครื่องเสมือนนี้โหลดด้วยเครื่องมือการพัฒนาทั้งหมดที่คุณต้องการ โดยมีไดเรกทอรีหน้าแรกขนาด 5 GB ถาวรและทํางานอยู่ใน Google Cloud ซึ่งช่วยปรับปรุงประสิทธิภาพและการตรวจสอบสิทธิ์ของเครือข่ายได้อย่างมาก ไม่เพียงเท่านั้น งานของคุณในห้องทดลองนี้ก็ทําได้โดยใช้เพียงเบราว์เซอร์หรือ Google Chromebook
เมื่อเชื่อมต่อกับ Cloud Shell แล้ว ระบบจะตรวจสอบว่าคุณตรวจสอบสิทธิ์แล้ว และมีการตั้งค่าโปรเจ็กต์เป็น PROJECT_ID แล้ว
เรียกใช้คําสั่งต่อไปนี้ใน Cloud Shell เพื่อยืนยันว่าคุณได้รับการตรวจสอบสิทธิ์
gcloud auth list
เอาต์พุตจากคําสั่ง
Credentialed accounts: - <myaccount>@<mydomain>.com (active)
gcloud config list project
เอาต์พุตจากคําสั่ง
[core] project = <PROJECT_ID>
หากไม่ใช่ คุณจะตั้งค่าได้โดยใช้คําสั่งนี้
gcloud config set project <PROJECT_ID>
เอาต์พุตจากคําสั่ง
Updated property [core/project].
ไปที่ UI ของโปรแกรมสร้างโปรไฟล์ใน Cloud Console โดยคลิกที่ "Profiler" ในแถบนําทางด้านซ้าย
หรืออีกทางเลือกหนึ่งคือ คุณสามารถใช้แถบค้นหาของ Cloud Console เพื่อไปที่ UI ของ Profiler ได้ง่ายๆ เพียงพิมพ์ "Stackdriver Profiler" แล้วเลือกรายการที่พบ ไม่ว่าคุณจะเลือกวิธีใด คุณจะเห็น UI ของ Profiler ที่มีข้อความ "ไม่มีข้อมูลที่จะแสดง" ดังตัวอย่างด้านล่าง โครงการนี้เป็นโครงการใหม่ จึงยังไม่มีการรวบรวมข้อมูลทําโปรไฟล์
ได้เวลาสร้างโปรไฟล์แล้วสิ!
เราจะใช้แอปพลิเคชัน Synthetic Go แบบง่ายใน GitHub ในเทอร์มินัล Cloud Shell ที่คุณยังเปิด (และขณะ &&tt; ไม่มีข้อมูลที่จะแสดง" ข้อความยังคงปรากฏใน UI ของ Profiler) ให้เรียกใช้คําสั่งต่อไปนี้
$ go get -u github.com/GoogleCloudPlatform/golang-samples/profiler/...
จากนั้นจึงเปลี่ยนเป็นไดเรกทอรีแอปพลิเคชัน
$ cd ~/gopath/src/github.com/GoogleCloudPlatform/golang-samples/profiler/hotapp
ไดเรกทอรีมีไฟล์ "main.go" ซึ่งเป็นแอปสังเคราะห์ที่เปิดใช้ Agent การทําโปรไฟล์
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)
}
...
}
Agent การทําโปรไฟล์จะรวบรวมโปรไฟล์ CPU, ฮีป และชุดข้อความโดยค่าเริ่มต้น โค้ดที่นี่จะเปิดใช้การรวบรวมโปรไฟล์ onclickx (หรือที่เรียกว่า "contention")
ต่อไป ให้เรียกใช้โปรแกรม:
$ go run main.go
ขณะที่โปรแกรมทํางาน Agent ที่ใช้รวบรวมข้อมูลจะรวบรวมโปรไฟล์ทั้ง 5 ประเภทที่กําหนดค่าไว้เป็นระยะๆ คอลเล็กชันจะได้รับการสุ่มเมื่อเวลาผ่านไป (โดยมีอัตราเฉลี่ย 1 โปรไฟล์ต่อนาทีสําหรับแต่ละประเภท) ดังนั้นระบบอาจใช้เวลาถึง 3 นาทีในการรวบรวมแต่ละประเภท โปรแกรมจะบอกคุณได้เมื่อสร้างโปรไฟล์ ข้อความจะเปิดใช้งานโดยแฟล็ก DebugLogging
ในการกําหนดค่าข้างต้น มิฉะนั้น Agent จะทํางานแบบเงียบ
$ 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 จะอัปเดตตัวเองทันทีหลังจากที่มีการรวบรวมโปรไฟล์ครั้งแรก และจะไม่อัปเดตโดยอัตโนมัติหลังจากนั้น หากต้องการดูข้อมูลใหม่ คุณจะต้องรีเฟรช UI เครื่องมือสร้างโปรไฟล์ด้วยตนเอง โดยให้คลิกปุ่ม "เลย" ในเครื่องมือเลือกช่วงเวลา 2 ครั้ง แล้วทําดังนี้
หลังจากรีเฟรช UI คุณจะเห็นดังนี้
ตัวเลือกประเภทโปรไฟล์จะแสดงโปรไฟล์ 5 ประเภทดังนี้
ตอนนี้มาตรวจสอบโปรไฟล์แต่ละประเภทและความสามารถที่สําคัญเกี่ยวกับ UI แล้วทําการทดสอบกัน ในขั้นตอนนี้ คุณไม่จําเป็นต้องใช้เทอร์มินัล Cloud Shell อีกต่อไป คุณจึงออกโดยกด Ctrl-C และพิมพ์ "exit"
ตอนนี้เรารวบรวมข้อมูลบางส่วนแล้ว ต่อไปมาดูรายละเอียดกัน เราใช้แอปสังเคราะห์ (แหล่งที่มาพร้อมใช้งานใน GitHub) เพื่อจําลองลักษณะการทํางานของปัญหาประสิทธิภาพประเภทต่างๆ ในเวอร์ชันที่ใช้งานจริง
โค้ดที่ใช้ CPU เป็นหลัก
เลือกประเภทโปรไฟล์ CPU หลังจากที่ UI โหลดแล้ว คุณจะเห็นกราฟ Leaf จํานวน 4 บล็อกสําหรับฟังก์ชัน load
ซึ่งจะปรากฏใน Flagment ซึ่งเรียกรวมกันว่าการใช้งาน CPU ทั้งหมด ดังนี้
ฟังก์ชันนี้เขียนขึ้นโดยเฉพาะเพื่อใช้ประโยชน์จากวงจร CPU จํานวนมากโดยการเรียกใช้รายงานแบบวนซ้ํา
main.go
func load() {
for i := 0; i < (1 << 20); i++ {
}
}
ระบบจะเรียกฟังก์ชันโดยอ้อมจาก busyloop
() ผ่านเส้นทางการโทร 4 เส้นทาง: busyloop
→ {foo1
, foo2
} → {bar
, baz
} → load
ความกว้างของกล่องฟังก์ชันจะแสดงค่าใช้จ่ายที่เกี่ยวข้องของเส้นทางการโทรที่เฉพาะเจาะจง ในกรณีนี้ เส้นทางทั้ง 4 เส้นทางมีค่าใช้จ่ายเท่ากัน ในโปรแกรมจริง คุณต้องเน้นไปที่การเพิ่มประสิทธิภาพเส้นทางการโทรที่มีความสําคัญมากที่สุดในแง่ของประสิทธิภาพ กราฟเปลวไฟจะเน้นเส้นทางที่ราคาแพงกว่าที่มีกล่องขนาดใหญ่กว่า ทําให้ระบุเส้นทางเหล่านี้ได้ง่าย
คุณจะใช้ตัวกรองข้อมูลโปรไฟล์เพื่อปรับแต่งการแสดงผลเพิ่มเติมได้ เช่น ลองเพิ่ม "Show Stack" ตัวกรองที่ระบุ "baz" เป็นสตริงตัวกรอง คุณควรเห็นตัวอย่างภาพหน้าจอด้านล่างนี้ ซึ่งจะแสดงเส้นทางการโทรไปยัง load()
เพียง 2 เส้นทางจากทั้งหมด 4 เส้นทาง เส้นทางทั้ง 2 เส้นนี้เป็นเส้นทางที่ผ่านฟังก์ชันที่มีสตริง "baz" อยู่ในชื่อเท่านั้น การกรองประเภทนี้จะมีประโยชน์เมื่อคุณต้องการมุ่งเน้นที่ส่วนย่อยของโปรแกรมที่ใหญ่กว่า (เช่น เนื่องจากคุณเป็นเจ้าของเพียงบางส่วนของโปรแกรมนั้น)
โค้ดที่ใช้หน่วยความจํามาก
เปลี่ยนประเภทโปรไฟล์ฮีปแล้ว &Hetp" ตรวจสอบว่าได้นําตัวกรองที่สร้างไว้ในการทดสอบก่อนหน้าออก ตอนนี้คุณจะเห็นกราฟเปลวไฟที่ allocImpl
ซึ่งเรียกโดย alloc
แสดงเป็นผู้บริโภคหลักของหน่วยความจําในแอป ดังนี้
ตารางสรุปข้อมูลเหนือกราฟเปลวไฟบ่งบอกว่าปริมาณหน่วยความจําที่ใช้ไปทั้งหมดในแอปอยู่ที่ประมาณ 57.4 MiB โดยเฉลี่ย ซึ่งส่วนมากแล้วจะจัดสรรโดยฟังก์ชัน allocImpl
ซึ่งก็ไม่น่าแปลกใจแต่อย่างใดหากคุณติดตั้งใช้งานฟังก์ชันนี้
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))
}
}
ฟังก์ชันนี้จะดําเนินการ 1 ครั้ง โดยจะจัดสรร 64 MB เป็นส่วนเล็กๆ แล้วจัดเก็บตัวชี้ไปยังกลุ่มเหล่านั้นในตัวแปรร่วมเพื่อป้องกันการเก็บขยะ โปรดทราบว่าปริมาณหน่วยความจําที่แสดงว่าเครื่องมือสร้างโปรไฟล์ใช้ไปนั้นแตกต่างจาก 64 MiB เล็กน้อย: เครื่องมือสร้างโปรไฟล์ Go ฮีปเป็นเครื่องมือทางสถิติ ดังนั้นการวัดจะมีโอเวอร์เฮดต่ําแต่ไม่ใช่ไบต์ข้อมูล อย่าประหลาดใจเมื่อเห็นความแตกต่างประมาณ 10% เช่นนี้
โค้ดที่ใช้ทรัพยากรจํานวนมาก (IO)
หากเลือก "Threads" ในตัวเลือกประเภทโปรไฟล์ จอแสดงผลจะเปลี่ยนเป็นกราฟเปลวไฟที่ความกว้างส่วนใหญ่ใช้โดยฟังก์ชัน wait
และ waitImpl
:
ในสรุปกราฟฟลัดไลท์ด้านบน คุณดูได้ว่ามีเทคโนโลยี 100 ที่เข้มงวดซึ่งทําให้สแต็กการเรียกใช้เพิ่มขึ้นจากฟังก์ชัน wait
โค้ดที่เริ่มต้นรอเหล่านี้มีลักษณะดังนี้
main.go
func main() {
...
// Simulate some waiting goroutines.
for i := 0; i < 100; i++ {
go wait()
}
โปรไฟล์ประเภทนี้มีประโยชน์ในการทําความเข้าใจว่าโปรแกรมใช้เวลารอไม่ตรงกับที่คาดไว้หรือไม่ (เช่น I/O) โดยทั่วไปแล้ว สแต็กการเรียกใช้ดังกล่าวจะไม่ถูกสุ่มตัวอย่างโดยเครื่องมือสร้างโปรไฟล์ CPU เนื่องจากไม่ได้กินเวลาส่วนสําคัญของ CPU คุณมักจะต้องใช้ "ซ่อนสแต็กและโควต้าของตัวกรองด้วยโปรไฟล์เทรด ตัวอย่างเช่น เพื่อซ่อนสแต็กทั้งหมดที่ลงท้ายด้วยการเรียกไปยัง gopark,
เนื่องจากคีย์เวิร์ดดังกล่าวมักไม่มีการใช้งานแบบเข้มงวดและไม่ค่อยสนใจในการรอ
ประเภทโปรไฟล์ชุดข้อความจะช่วยระบุจุดในโปรแกรมที่ชุดข้อความกําลังรอเครื่องหมายx ซึ่งเป็นส่วนอื่นของโปรแกรมเป็นระยะเวลานาน แต่ประเภทโปรไฟล์ต่อไปนี้จะเป็นประโยชน์มากกว่า
โค้ดที่เน้นเนื้อหา
ประเภทโปรไฟล์การโต้แย้งจะระบุการล็อก "wantt" สูงสุดในโปรแกรม โปรไฟล์ประเภทนี้พร้อมใช้งานสําหรับโปรแกรม Go แต่ต้องเปิดใช้อย่างชัดแจ้งโดยระบุ "MutexProfiling: true
" ในโค้ดการกําหนดค่า Agent คอลเล็กชันทํางานโดยการบันทึก (ภายใต้เมตริก "Contentions") จํานวนครั้งที่แม่กุญแจที่เฉพาะเจาะจงเมื่อปลดล็อกโดย A ที่เข้มงวดมี Butut อื่นๆ ที่รอการล็อค อีกทั้งยังบันทึก (ภายใต้เมตริก "Delay" ) เวลาที่รอการบล็อกโดยที่เข้มงวด ในตัวอย่างนี้ มีสแต็กการโต้แย้ง 1 รายการ และเวลารอทั้งหมดของล็อกคือ 11.03 วินาที
โค้ดที่สร้างโปรไฟล์นี้จะประกอบด้วยตัวระบุที่มีประสิทธิภาพ 4 รายการซึ่งจะต่อสู้กันด้วยตัวเอง
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)
}
}
ในห้องทดลองนี้ คุณได้เรียนรู้วิธีกําหนดค่าโปรแกรม Go เพื่อใช้กับ Stackdriver Profiler แล้ว และคุณได้เรียนรู้วิธีรวบรวม ดู และวิเคราะห์ข้อมูลประสิทธิภาพด้วยเครื่องมือนี้แล้ว ตอนนี้คุณนําทักษะใหม่ไปใช้กับบริการจริงที่คุณเรียกใช้ใน Google Cloud Platform ได้แล้ว
คุณได้เรียนรู้วิธีกําหนดค่าและใช้ Stackdriver Profiler แล้ว
ดูข้อมูลเพิ่มเติม
- โปรไฟล์ Stackdriver: https://cloud.google.com/profiler/
- แพ็กเกจรันไทม์/pprof ที่ Stackdriver Profiler ใช้: https://golang.org/pkg/runtime/pprof/
ใบอนุญาต
ผลงานนี้ได้รับอนุญาตภายใต้สัญญาอนุญาตทั่วไปของ Creative Commons Attribution 2.0