เคล็ดลับ Go Unit Testing Performance & How to Debug
วันนี้เราจะมาเรียนการเขียนโค้ดแบบง่าย ๆ ด้วย Golang และใช้ Go Testing Package ในการเขียน Unit Test เพื่อวัดประสิทธิภาพการทำงานของโค้ดของเรา พร้อมกับแสดงวิธีการ Debug เวลาที่โปรแกรมเกิดปัญหาโดยใช้ Visual Studio Code
มาเริ่มต้นด้วยการสร้างโปรแกรมกัน
ในบทความนี้เราจะมาเขียนโปรแกรมคำนวณเพื่อหาว่าเลขที่เราใส่ไปเป็นจำนวนเฉพาะ (Prime Number = เลขจำนวนเต็มที่ไม่สามารถหารด้วยตัวเลขอื่นได้นอกจากเลข 1 และตัวมันเอง) หรือไม่ โดย Input จะเป็นเลขจำนวนเต็ม 1 จำนวน ถ้าจำนวนดังกล่าวเป็นจำนวนเฉพาะจริง Output จะ Return True แต่ถ้าไม่ใช่จะ Return False
Step 1: สร้าง Work Directory
ใช้คำสั่ง mkdir ในการสร้าง Directory ชื่อ isPrime
$ mkdir ./isPrime
Step 2: สร้างไฟล์
เลือก Work Directory ที่เราสร้างไว้ในขั้นตอนที่ 1 และกดสร้างไฟล์ โดยตั้งชื่อไฟล์ว่า isPrime.go
Step 3: เพิ่มโค้ด
ภายในไฟล์จะประกอบไปด้วย Source Code ดังนี้
(1) Method isPrime รับ Parameter 1 ตัว คือ x ที่มี Type ของข้อมูลเป็น Int และจะคำนวณหาว่า x เป็นจำนวนเฉพาะหรือไม่ ถ้าใช่จะ Return True ถ้าไม่ใช่ Return False
(2) Method Main จะทำหน้าที่เรียก Method isPrime โดยส่งจำนวนเต็มเข้าไป ในตัวอย่างนี้คือ 13 และจะปริ้นท์ผลลัพธ์ออกมาว่าเป็น True หรือ False
Step 4: Go Mod Init & Go Mod Tidy
สร้าง Go Mod File เพื่อเก็บ Dependencies
$ go mod init tipsTest// then go mod tidy to add module requirements and sums$ go mod tidy
Step 5: สั่งรัน
ลองสั่งรันด้วยคำสั่งด้านล่างนี้
$ go run isPrime.go
จะได้ผลลัพธ์ตามภาพ
เขียน Unit Testing
Unit Testing คือการเขียนโปรแกรมเพื่อทดสอบการทำงานในแต่ละ Module ให้เรามั่นใจว่าโปรแกรมสามารถทำงานได้อย่างถูกต้อง
Step 1: สร้าง Test File
ชื่อไฟล์จะต้องลงท้ายด้วยคำว่า _test.go
เช่น isPrime_test.go
Step 2: สร้าง Function
สร้าง Function สำหรับเขียน Test โดยตั้งชื่อตามฟอร์แมต func TestXxxx(t *testing.T)
- ชื่อ Function ต้องขึ้นต้นด้วยคำว่า
Test
- ตามด้วยตัวอักษรตัวแรกของคำเป็นตัวพิมพ์ใหญ่
- Function จะรับ Input แค่ t *testing.T
func TestIsPrime(t *testing.T) {
//body
}
Step 3: เพิ่มโค้ดเพื่อเรียกใช้งาน isPrime Function
TestIsPrime Function จะเรียก isPrime Function ที่เราประกาศไว้ก่อนหน้านี้ใน Package Main
Step 4: Run the Test
ลองสั่งรันด้วยคำสั่ง
$ go test
จะได้ผลลัพธ์ตามภาพ
// output
PASS
ok tipsTest 0.404s
หรือกด run test
จะได้ผลลัพธ์ออกมาเป็น Ok
PASS หมายถึงโค้ดสามารถทำงานได้ถูกต้องตามที่คาดหวังไว้ แต่ถ้าเทสแล้วไม่ผ่านจะแสดงผลลัพธ์ FAIL
ถ้าอยากเห็นว่าแต่ละเทสที่รันใช้เวลาเท่าไหร่ ให้เพิ่ม Flag -v เข้าไป
$ go test -v
จะได้ผลลัพธ์ตามภาพ
// output
=== RUN TestIsPrime
--- PASS: TestIsPrime (0.00s)
PASS
ok tipsTest 0.134s
เริ่มเขียน Benchmarks
ทีนี้มาถึงการทำ Benchmark เพื่อวัดประสิทธิภาพการทำงานของโค้ดที่เขียนขึ้นมาว่า Performance เป็นอย่างไร
Step 1: สร้าง Benchmark Function
ตั้งชื่อ func BenchmarkXxx(*testing.B)
- ชื่อของ Function ต้องขึ้นต้นด้วยคำว่า
Benchmark
- คำที่ตามหลังตัวอักษรตัวแรกของคำต้องเป็นตัวพิมพ์ใหญ่
- รับ Input (*testing.B)
func BenchmarkIsPrime(b *testing.B) {
for i := 0; i < b.N; i++ {
isPrime(9897)
}
}
Step 2: รัน Benchmark
$ go test -bench=.// output
goos: darwin
goarch: amd64
pkg: tipsTest
cpu: Intel(R) Core(TM) i5-1038NG7 CPU @ 2.00GHz
BenchmarkIsPrime-8 192724854 6.221 ns/op
PASS
ok tipsTest 2.233s
ผลลัพธ์จะแสดงให้เห็นว่าลูปไปทั้งสิ้น 192,724,854 ครั้ง และใช้เวลา 6.221 Nanosecond ต่อลูป
. จะรันทุก ๆ Benchmark Function ในไฟล์
Step 3: วัด Performance
เพิ่มประสิทธิภาพด้วยการทดลองเขียนวิธีการคำนวณจำนวนเฉพาะด้วยวิธีอื่น โดยสร้างไฟล์ใหม่ชื่อ isPrime2.go
และสร้าง Function isPrime2
จากนั้นเพิ่มโค้ดใน IsPrimeBenchmark_test.go
ดังรูป
ลองรัน Test อีกครั้งจะได้ผลลัพธ์ตามนี้
$ go test -bench=.//output
goos: darwin
goarch: amd64
pkg: tipsTest
cpu: Intel(R) Core(TM) i5-1038NG7 CPU @ 2.00GHz
BenchmarkIsPrime-8 193049048 6.229 ns/op 0 B/op 0 allocs/op
BenchmarkIsPrime2-8 193192003 6.223 ns/op 0 B/op 0 allocs/op
PASS
ok tipsTest 3.781s
จะเห็นว่าผลลัพธ์ของ BenchmarkIsPrime2 เทียบกับ BenchmarkIsPrime นั้นใช้เวลาต่างกัน โดย BenchmarkIsPrime2 ใช้เวลา 6.223 ns/op
ในขณะที่ BenchmarkIsPrime ใช้เวลา 6.229 ns/op
เพียงเท่านี้เราก็สามารถทำการวัดประสิทธิภาพของโค้ดที่เราพัฒนาขึ้นมาได้ว่าเป็นตามที่เราต้องการหรือไม่
เริ่ม Debug โค้ดโดยใช้ Visual Studio Code
เราจะใช้ Breakpoints ในการ Debug Error ซึ่งการ Debug จะแสดงค่าของ Variable ในแต่ละ State ที่โค้ดถูกรัน
Step 1: ติดตั้ง dlv-dap
รันคำสั่งด้านล่างเพื่อติดตั้ง
$ brew install go-delve/delve/delve
Step 2: Debugging With Breakpoints
คลิกด้านหน้าเลขบรรทัดของโค้ดเพื่อเพิ่ม Breakpoints ในกรณีนี้เราจะใส่ Breakpoints ที่บรรทัดที่ 14 ก่อนเข้า For Loop
Step 3: เริ่ม Debug Test
เลือก Debug Test เพื่อรัน
เมื่อรันมาถึงบรรทัดที่ 14 เจอ Breakpoints โปรแกรมจะหยุด Pause ตรงนี้ ให้สังเกต Panel ด้านซ้ายมือ (กรอบสีเหลือง) จะเห็นค่าตัวแปรและ State ต่าง ๆ เราสามารถกด Forward ในกรอบสีแดงให้รันโปรแกรมบรรทัดถัดไปเพื่อ Debug ต่อ หรือกดไอคอนด้านซ้ายของกรอบสีแดงเพื่อรันต่อไปเรื่อย ๆ จนไปหยุดที่ Breakpoints
หากต้องการดูตัวอย่างโค้ด สามารถเข้าไปดูที่ GitHub ด้านล่างนี้ได้เลยค่ะ
หวังว่าบทความนี้จะเป็นประโยชน์ต่อผู้พัฒนาที่อยากลองเริ่มเขียน Unit Testing และวัดประสิทธิภาพโปรแกรมที่เราเขียน หากคุณชอบบทความของเรา รบกวนกด “Claps” 👏 เพื่อเป็นกำลังใจให้เราเขียนบทความต่อไปด้วยนะคะ ขอบคุณค่ะ
References
สำหรับชาวเทคคนไหนที่สนใจเรื่องราวดีๆแบบนี้ หรืออยากเรียนรู้เกี่ยวกับ Product ใหม่ๆ ของ KBTG สามารถติดตามรายละเอียดกันได้ที่เว็บไซต์ www.kbtg.tech