วิเคราะห์ประสิทธิภาพการผลิตด้วย Cloud Profiler

1. ภาพรวม

แม้ว่านักพัฒนาแอปไคลเอ็นต์และเว็บฟรอนท์เอนด์มักจะใช้เครื่องมืออย่างเครื่องมือสร้างโปรไฟล์ CPU ของ Android Studio หรือเครื่องมือทำโปรไฟล์ที่รวมอยู่ใน Chrome เพื่อปรับปรุงประสิทธิภาพของโค้ด แต่ผู้ที่ทำงานบนบริการแบ็กเอนด์ยังไม่มีเทคนิคที่เทียบเท่ากัน Cloud Profiler นำความสามารถเดียวกันนี้มาสู่นักพัฒนาซอฟต์แวร์บริการ โดยไม่คำนึงว่าโค้ดของพวกเขาจะทำงานบน Google Cloud Platform หรือที่อื่นๆ

95c034c70c9cac22.png

เครื่องมือจะรวบรวมข้อมูลการใช้งาน CPU และการจัดสรรหน่วยความจำจากแอปพลิเคชันเวอร์ชันที่ใช้งานจริง โดยจะระบุแหล่งที่มาของข้อมูลดังกล่าวเป็นซอร์สโค้ดของแอปพลิเคชัน ซึ่งจะช่วยให้คุณระบุส่วนต่างๆ ของแอปพลิเคชันที่ใช้ทรัพยากรมากที่สุด และทำให้ประสิทธิภาพการทำงานของโค้ดโดดเด่นยิ่งขึ้น โอเวอร์เฮดน้อยของเทคนิคการรวบรวมข้อมูลที่เครื่องมือใช้ทำให้เหมาะสำหรับการใช้งานอย่างต่อเนื่องในสภาพแวดล้อมการผลิต

ใน Codelab นี้ คุณจะได้ดูวิธีตั้งค่า Cloud Profiler สำหรับโปรแกรม Go และจะได้ทำความคุ้นเคยกับข้อมูลเชิงลึกเกี่ยวกับประสิทธิภาพของแอปพลิเคชันที่เครื่องมือสามารถนำเสนอได้

สิ่งที่คุณจะได้เรียนรู้

  • วิธีกำหนดค่าโปรแกรม Go สำหรับการทำโปรไฟล์ด้วย Cloud Profiler
  • วิธีรวบรวม ดู และวิเคราะห์ข้อมูลประสิทธิภาพด้วย Cloud Profiler

สิ่งที่คุณต้องมี

  • โปรเจ็กต์ Google Cloud Platform
  • เบราว์เซอร์ เช่น Chrome หรือ Firefox
  • คุ้นเคยกับเครื่องมือแก้ไขข้อความมาตรฐานของ Linux เช่น Vim, EMACs หรือ Nano

คุณจะใช้บทแนะนำนี้อย่างไร

อ่านเท่านั้น อ่านและทำแบบฝึกหัด

คุณจะให้คะแนนประสบการณ์การใช้งาน Google Cloud Platform อย่างไร

มือใหม่ ระดับกลาง ผู้ชำนาญ

2. การตั้งค่าและข้อกำหนด

การตั้งค่าสภาพแวดล้อมตามเวลาที่สะดวก

  1. ลงชื่อเข้าใช้ Cloud Console และสร้างโปรเจ็กต์ใหม่หรือใช้โปรเจ็กต์ที่มีอยู่ซ้ำ หากยังไม่มีบัญชี Gmail หรือ Google Workspace คุณต้องสร้างบัญชี

96a9c957bc475304.png

b9a10ebdf5b5a448.png

a1e3c01a38fa61c2.png

โปรดจดจำรหัสโปรเจ็กต์ ซึ่งเป็นชื่อที่ไม่ซ้ำกันในโปรเจ็กต์ Google Cloud ทั้งหมด (ชื่อด้านบนมีคนใช้แล้ว และจะใช้ไม่ได้ ขออภัย) และจะมีการอ้างอิงใน Codelab ว่า PROJECT_ID ในภายหลัง

  1. ถัดไป คุณจะต้องเปิดใช้การเรียกเก็บเงินใน Cloud Console เพื่อใช้ทรัพยากร Google Cloud

การใช้งาน Codelab นี้น่าจะไม่มีค่าใช้จ่ายใดๆ หากมี ตรวจสอบว่าคุณได้ทำตามวิธีการใน "การล้างข้อมูล" ซึ่งจะแนะนำคุณเกี่ยวกับวิธีปิดทรัพยากรเพื่อไม่ให้มีการเรียกเก็บเงินนอกเหนือจากบทแนะนำนี้ ผู้ใช้ใหม่ของ Google Cloud จะมีสิทธิ์เข้าร่วมโปรแกรมทดลองใช้ฟรี$300 USD

Google Cloud Shell

แม้ว่าคุณจะดำเนินการ Google Cloud จากระยะไกลได้จากแล็ปท็อป แต่เราจะใช้ Google Cloud Shell ซึ่งเป็นสภาพแวดล้อมบรรทัดคำสั่งที่ทำงานในระบบคลาวด์เพื่อให้การตั้งค่าง่ายขึ้นใน Codelab

เปิดใช้งาน Cloud Shell

  1. คลิกเปิดใช้งาน Cloud Shell 4292cbf4971c9786.png จาก Cloud Console

bce75f34b2c53987.png

หากคุณไม่เคยเริ่มต้นใช้งาน Cloud Shell มาก่อน คุณจะเห็นหน้าจอตรงกลาง (ครึ่งหน้าล่าง) ซึ่งอธิบายว่านี่คืออะไร หากเป็นเช่นนั้น ให้คลิกดำเนินการต่อ (คุณจะไม่เห็นการดำเนินการนี้อีก) หน้าจอแบบครั้งเดียวมีลักษณะดังนี้

70f315d7b402b476.png

การจัดสรรและเชื่อมต่อกับ Cloud Shell ใช้เวลาเพียงไม่กี่นาที

fbe3a0674c982259.png

เครื่องเสมือนนี้เต็มไปด้วยเครื่องมือการพัฒนาทั้งหมดที่คุณต้องการ โดยมีไดเรกทอรีหลักขนาด 5 GB ที่ทำงานอย่างต่อเนื่องใน Google Cloud ซึ่งจะช่วยเพิ่มประสิทธิภาพของเครือข่ายและการตรวจสอบสิทธิ์ได้อย่างมาก งานส่วนใหญ่ใน Codelab นี้สามารถทำได้โดยใช้เบราว์เซอร์หรือ Chromebook เท่านั้น

เมื่อเชื่อมต่อกับ Cloud Shell คุณควรเห็นว่าได้รับการตรวจสอบสิทธิ์แล้ว และโปรเจ็กต์ได้รับการตั้งค่าเป็นรหัสโปรเจ็กต์แล้ว

  1. เรียกใช้คำสั่งต่อไปนี้ใน Cloud Shell เพื่อยืนยันว่าคุณได้รับการตรวจสอบสิทธิ์แล้ว
gcloud auth list

เอาต์พุตจากคำสั่ง

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

To set the active account, run:
    $ gcloud config set account `ACCOUNT`
  1. เรียกใช้คำสั่งต่อไปนี้ใน Cloud Shell เพื่อยืนยันว่าคำสั่ง gcloud รู้เกี่ยวกับโปรเจ็กต์ของคุณ
gcloud config list project

เอาต์พุตจากคำสั่ง

[core]
project = <PROJECT_ID>

หากไม่ใช่ ให้ตั้งคำสั่งด้วยคำสั่งนี้

gcloud config set project <PROJECT_ID>

เอาต์พุตจากคำสั่ง

Updated property [core/project].

3. ไปที่ Cloud Profiler

ใน Cloud Console ให้ไปที่ UI เครื่องมือสร้างโปรไฟล์โดยคลิก "Profiler" ในแถบนำทางด้านซ้าย

37ad0df7ddb2ad17.png

หรือจะใช้แถบค้นหา Cloud Console เพื่อไปยัง UI ของเครื่องมือสร้างโปรไฟล์ เพียงพิมพ์ "Cloud Profiler" แล้วเลือกรายการที่พบ ไม่ว่าคุณจะเลือกแบบใด คุณควรเห็น UI เครื่องมือสร้างโปรไฟล์พร้อมข้อความ "ไม่มีข้อมูลที่จะแสดง" ดังตัวอย่างด้านล่าง โปรเจ็กต์นี้เป็นโปรเจ็กต์ใหม่ จึงยังไม่มีการเก็บข้อมูลการทำโปรไฟล์ไว้

d275a5f61ed31fb2.png

ได้เวลาสร้างโปรไฟล์แล้ว

4. สร้างโปรไฟล์การเปรียบเทียบ

เราจะใช้แอปพลิเคชัน Go สังเคราะห์อย่างง่ายที่มีอยู่ใน GitHub ในเทอร์มินัล Cloud Shell ที่คุณยังเปิดอยู่ (และในขณะที่ข้อความ "ไม่มีข้อมูลที่จะแสดง" ยังคงแสดงอยู่ใน UI เครื่องมือสร้างโปรไฟล์) ให้เรียกใช้คำสั่งต่อไปนี้

$ 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, ฮีป และเทรดโดยค่าเริ่มต้น โค้ดในที่นี้จะเปิดใช้การรวบรวมโปรไฟล์ Mutex (หรือที่เรียกว่า "contention")

เรียกใช้โปรแกรมเลย ดังนี้

$ go run main.go

ขณะที่โปรแกรมทำงาน Agent การสร้างโปรไฟล์จะรวบรวมโปรไฟล์ของทั้ง 5 ประเภทที่กำหนดค่าไว้เป็นระยะๆ การเก็บรวบรวมข้อมูลจะเป็นแบบสุ่มเมื่อเวลาผ่านไป (ด้วยอัตราเฉลี่ยของ 1 โปรไฟล์ต่อนาทีสำหรับแต่ละประเภท) ดังนั้นอาจใช้เวลานานถึง 3 นาทีในการรวบรวมข้อมูลแต่ละประเภท โปรแกรมจะแจ้งให้คุณทราบเมื่อสร้างโปรไฟล์ ข้อความเปิดใช้โดยแฟล็ก DebugLogging ในการกำหนดค่าข้างต้น มิฉะนั้น ตัวแทนจะทำงานโดยไม่มีการแจ้งเตือน:

$ 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 ครั้ง

650051097b651b91.png

หลังจากรีเฟรช UI คุณจะเห็นดังนี้

47a763d4dc78b6e8.png

ตัวเลือกประเภทโปรไฟล์แสดงโปรไฟล์ 5 ประเภทที่ใช้ได้ ดังนี้

b5d7b4b5051687c9.png

มาดูโปรไฟล์แต่ละประเภทและความสามารถด้าน UI ที่สำคัญบางอย่าง จากนั้นจึงทำการทดสอบ ในขั้นตอนนี้ คุณไม่จำเป็นต้องใช้เทอร์มินัล Cloud Shell อีกต่อไป คุณสามารถออกโดยการกด Ctrl-C และพิมพ์ "exit"

5. วิเคราะห์ข้อมูลเครื่องมือสร้างโปรไฟล์

เมื่อเรารวบรวมข้อมูลบางส่วนไปแล้ว เรามาดูรายละเอียดกัน เราใช้แอปสังเคราะห์ (แหล่งที่มานี้มีให้ใช้งานใน GitHub) ซึ่งจำลองพฤติกรรมทั่วไปของปัญหาด้านประสิทธิภาพประเภทต่างๆ ในเวอร์ชันที่ใช้งานจริง

โค้ดแบบ CPU

เลือกประเภทโปรไฟล์ CPU หลังจาก UI โหลดแล้ว คุณจะเห็นบล็อก 4 แฉกของฟังก์ชัน load ในกราฟ Flame ซึ่งพิจารณาการใช้ CPU ทั้งหมดรวมกัน ดังนี้

fae661c9fe6c58df.png

ฟังก์ชันนี้เขียนขึ้นเพื่อให้ใช้รอบ CPU สูงโดยเฉพาะด้วยการเรียกใช้ลูปจำกัด:

main.go

func load() {
        for i := 0; i < (1 << 20); i++ {
        }
}

ระบบจะเรียกฟังก์ชันนี้โดยอ้อมจาก busyloop() ผ่านทางเส้นทางการเรียก 4 เส้นทาง ได้แก่ busyloop → {foo1, foo2} → {bar, baz} → load ความกว้างของกล่องฟังก์ชันแสดงถึงต้นทุนสัมพัทธ์ของเส้นทางการเรียกใช้ที่เฉพาะเจาะจง ในกรณีนี้ทั้ง 4 เส้นทางมีค่าใช้จ่ายเท่ากัน ในโปรแกรมจริง คุณต้องการเน้นการเพิ่มประสิทธิภาพเส้นทางการโทรที่มีความสำคัญมากที่สุดในแง่ของประสิทธิภาพ กราฟเปลวไฟซึ่งเน้นเส้นทางที่มีราคาแพงกว่าพร้อมด้วยกล่องที่ใหญ่กว่า ทำให้ระบุเส้นทางเหล่านี้ได้ง่าย

คุณสามารถใช้ตัวกรองข้อมูลโปรไฟล์เพื่อปรับแต่งการแสดงผลเพิ่มเติมได้ ตัวอย่างเช่น ลองเพิ่ม "แสดงกองรูปภาพ" ตัวกรองที่ระบุ "baz" เป็นสตริงตัวกรอง คุณควรจะเห็นบางอย่างเหมือนภาพหน้าจอด้านล่าง ซึ่งแสดงเส้นทางการโทรไปยัง load() เพียง 2 จาก 4 เส้นทางเท่านั้น เส้นทางทั้งสองนี้เป็นเพียงเส้นทางเดียวที่ไปยังฟังก์ชันที่มีสตริง "baz" ในชื่อ การกรองดังกล่าวมีประโยชน์เมื่อคุณต้องการโฟกัสที่ส่วนย่อยๆ ของโปรแกรมที่ใหญ่กว่า (เช่น เนื่องจากคุณเป็นเจ้าของเฉพาะบางส่วนของโปรแกรม)

eb1d97491782b03f.png

โค้ดที่ใช้หน่วยความจำมาก

เปลี่ยนเป็น "ฮีป" ประเภทโปรไฟล์ อย่าลืมนำตัวกรองที่คุณสร้างไว้ในการทดสอบก่อนหน้าออก ตอนนี้คุณจะเห็นกราฟเปลวไฟที่ allocImpl ซึ่งเรียกใช้โดย alloc แสดงเป็นผู้บริโภคหลักของหน่วยความจำในแอป

f6311c8c841d04c4.png

ตารางสรุปเหนือกราฟ Flame Fit ระบุว่าจำนวนหน่วยความจำที่ใช้ทั้งหมดในแอปโดยเฉลี่ยอยู่ที่ประมาณ 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))
        }
}

ฟังก์ชันนี้จะทำงานครั้งเดียว โดยจัดสรร 64 MiB เป็นส่วนย่อยที่เล็กลง จากนั้นจัดเก็บตัวชี้ไปยังชิ้นส่วนเหล่านั้นในตัวแปรร่วมเพื่อป้องกันการเก็บข้อมูลขยะ โปรดทราบว่าจำนวนหน่วยความจำที่เครื่องมือสร้างโปรไฟล์ใช้อยู่จะแตกต่างจาก 64 MiB เล็กน้อย เครื่องมือสร้างโปรไฟล์ฮีปของ Go เป็นเครื่องมือทางสถิติ การวัดจึงมีค่าใช้จ่ายแบบโอเวอร์เฮดต่ำ แต่ไม่แม่นยำเป็นไบต์ โปรดอย่าแปลกใจเมื่อเห็นความแตกต่าง ~10% เช่นนี้

โค้ดที่เน้น IO

หากคุณเลือก "ชุดข้อความ" ในตัวเลือกประเภทโปรไฟล์ การแสดงผลจะสลับเป็นกราฟเปลวไฟ ซึ่งฟังก์ชัน wait และ waitImpl จะใช้ความกว้างส่วนใหญ่

ebd57fdff01dede9.png

ในสรุปกราฟเปลวไฟด้านบน จะเห็นว่ามีโกรูทีน 100 ตัวที่เพิ่มจำนวนการเรียกจากฟังก์ชัน wait โค้ดนี้ถูกต้องทุกประการเนื่องจากโค้ดที่เริ่มการรอเหล่านี้จะมีลักษณะดังนี้

main.go

func main() {
        ...
        // Simulate some waiting goroutines.
        for i := 0; i < 100; i++ {
                go wait()
        }

โปรไฟล์ประเภทนี้มีประโยชน์ในการทำความเข้าใจว่าโปรแกรมใช้เวลารอแบบไม่คาดคิดหรือไม่ (เช่น I/O) โดยปกติแล้ว สแต็กการเรียกใช้ดังกล่าวจะไม่สุ่มตัวอย่างโดยเครื่องมือสร้างโปรไฟล์ CPU เนื่องจากไม่ได้ใช้เวลา CPU อย่างมาก คุณมักต้องการใช้ "ซ่อนกอง" ตัวกรองที่มีโปรไฟล์ "ชุดข้อความ" เช่น เพื่อซ่อนสแต็กทั้งหมดที่ลงท้ายด้วยการเรียกไปยัง gopark, เนื่องจากแท็กเหล่านั้นมักจะเป็นโกรูทีนที่ไม่มีความเคลื่อนไหวและน่าสนใจน้อยกว่าสแต็กที่รออยู่ใน I/O

ประเภทโปรไฟล์ของเทรดยังช่วยระบุจุดในโปรแกรมที่เทรดกำลังรอ Mutex ที่เป็นของส่วนอื่นของโปรแกรมมาเป็นเวลานาน แต่โปรไฟล์ประเภทต่อไปนี้จะมีประโยชน์มากกว่า

โค้ดที่มีการแข่งขันกันอย่างดุเดือด

ประเภทโปรไฟล์การโต้แย้งจะระบุรายการที่ "ต้องการ" มากที่สุด ล็อกในโปรแกรม โปรไฟล์ประเภทนี้ใช้ได้กับโปรแกรม Go แต่ต้องเปิดใช้อย่างชัดเจนโดยระบุ "MutexProfiling: true" ในรหัสการกำหนดค่า Agent การเก็บรวบรวมนี้ทำงานโดยการบันทึก (ภายใต้เมตริก "การช่วงชิง") จำนวนครั้งที่ล็อกตัวใดตัวหนึ่ง เมื่อถูกโกรูทีน A ปลดล็อก ตัวโกรูทีน B อีกตัวหนึ่งตัวขณะที่รอการปลดล็อก นอกจากนี้ยังบันทึก (ในเมตริก "ถ่วงเวลา") เวลาที่โกโรทีนที่ถูกบล็อกรอการล็อกด้วย ในตัวอย่างนี้ มีสแต็กเดี่ยวๆ และเวลารอรวมของการล็อกคือ 10.5 วินาที

83f00dca4a0f768e.png

โค้ดที่สร้างโปรไฟล์นี้ประกอบไปด้วยโกโรทีน 4 ตัวที่กำลังต่อสู้กันเหนือ 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. สรุป

ในห้องทดลองนี้ คุณได้เรียนรู้วิธีกำหนดค่าโปรแกรม Go ให้ใช้ร่วมกับ Cloud Profiler แล้ว และคุณยังได้เรียนรู้วิธีรวบรวม ดู และวิเคราะห์ข้อมูลประสิทธิภาพด้วยเครื่องมือนี้อีกด้วย ตอนนี้คุณใช้ทักษะใหม่กับบริการจริงที่ใช้ใน Google Cloud Platform ได้แล้ว

7. ยินดีด้วย

คุณได้เรียนรู้วิธีกำหนดค่าและใช้ Cloud Profiler แล้ว

ดูข้อมูลเพิ่มเติม

ใบอนุญาต

ผลงานนี้ได้รับอนุญาตภายใต้ใบอนุญาตทั่วไปครีเอทีฟคอมมอนส์แบบระบุแหล่งที่มา 2.0