- vừa được xem lúc

Test benchmark fibonaci recursive/non recursive

0 0 33

Người đăng: Ngọc Nguyễn Đức

Theo Viblo Asia

Test benchmark fibonaci recursive/non recursive

Động cơ, lý do

Trước giờ mình vẫn có thắc mắc là đối với cùng 1 bài toán giữa recursive và non recursive thì cái nào sẽ là cách giải quyết hiệu quả hơn. Nhân dịp mình có review code của các member trong team, mọi người có trao đổi về recursive/ non recursive nên mình quyết định tìm hiểu sâu hơn xem suy nghĩ từ đầu của mình đã đúng chưa.

Mình có tìm hiểu các tài liệu thì hầu như mọi người đều có chung nhận xét như sau:

Ưu điểm của đệ quy (recursive): Gọn gàng dễ hiểu, dễ viết code

Nhược điểm: Tốn không gian bộ nhớ, và thời gian xử lý do phải switch context giữa các lần gọi hàm.

Trên đây đều là những nhận xét mang tính lý thuyết. Với góc nhìn của một người hiện tại đang là một kỹ sư từng là học sinh chuyên lý, mình chỉ tin khi có kết quả thí nghiệm là những con số cụ thể.

Và thật may go giúp mình làm điều này khá dễ dàng bằng công cụ test benchmarks

Không nói nhiều nữa, chúng ta cùng vào chủ đề chính.

Test benchmarks với fibonaci recursive

Đầu tiên chúng ta thực hiện cài đặt thuật toán với cách tiếp cận là đệ quy.

Cài đặt test benchmark với đầu vào N = 10

package bench import ( "testing"
) func FibRecursive(n int) int { if n < 2 { return n } return FibRecursive(n-1) + FibRecursive(n-2)
} func BenchmarkFibRecursive10(b *testing.B) { // run the FibRecursive function b.N times for n := 0; n < b.N; n++ { FibRecursive(10) }
}

Thực hiện test benchmark bằng lệnh sau

go test -bench=. -benchmem -memprofile memprofile.out -cpuprofile profile.out

Kết quả

goos: darwin
goarch: amd64
pkg: github.com/hblab-ngocnd/csv-demo/bench
cpu: Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz
BenchmarkFibRecursive10-12 4973802 239.1 ns/op 0 B/op 0 allocs/op
PASS
ok github.com/hblab-ngocnd/csv-demo/bench 1.751s

Nhận xét: Lặp 4973802 lần, mỗi lần mất 239.1 ns tổng 1189236058.2 ns

Máy mình tận i7 lận, giờ mới để ý

Phân tích sâu hơn 1 tý

Phân tích thời gian chạy

Chạy lệnh

go tool pprof profile.out

Type: cpu
Time: Aug 25, 2022 at 3:12pm (+07)
Duration: 1.64s, Total samples = 1.23s (75.02%)
Entering interactive mode (type "help" for commands, "o" for options)
(pprof) top

Dùng lệnh top để lấy những xử lý mất thời gian nhất, sắp xếp theo thứ tự giảm dần

Kết quả

Showing nodes accounting for 1.23s, 100% of 1.23s total flat flat% sum% cum cum% 1.21s 98.37% 98.37% 1.21s 98.37% github.com/hblab-ngocnd/csv-demo/bench.FibRecursive 0.01s 0.81% 99.19% 1.22s 99.19% github.com/hblab-ngocnd/csv-demo/bench.BenchmarkFibRecursive10 0.01s 0.81% 100% 0.01s 0.81% runtime/pprof.(*profMap).lookup 0 0% 100% 0.01s 0.81% runtime/pprof.(*profileBuilder).addCPUData 0 0% 100% 0.01s 0.81% runtime/pprof.profileWriter 0 0% 100% 1.22s 99.19% testing.(*B).launch 0 0% 100% 1.22s 99.19% testing.(*B).runN

Mất tổng thời gian chạy cho FibRecursive1.21s

Tiếp tục phân tích sâu hơn từng dòng lệnh đối với hàm FibRecursive

(pprof) list FibRecursive

Kết qủa:

Total: 1.23s
ROUTINE ======================== github.com/hblab-ngocnd/csv-demo/bench.BenchmarkFibRecursive10 in /Users/S16593/Hblab/Motify/csv-demo/bench/Figo_test.go 10ms 1.22s (flat, cum) 99.19% of Total . . 11: return FibRecursive(n-1) + FibRecursive(n-2) . . 12:} . . 13: . . 14:func BenchmarkFibRecursive10(b *testing.B) { . . 15: // run the FibRecursive function b.N times 10ms 10ms 16: for n := 0; n < b.N; n++ { . 1.21s 17: FibRecursive(10) . . 18: } . . 19:} . . 20: . . 21://func FibNonRecursive(n int) int { . . 22:// if n < 2 {
ROUTINE ======================== github.com/hblab-ngocnd/csv-demo/bench.FibRecursive in /Users/S16593/Hblab/Motify/csv-demo/bench/Figo_test.go 1.21s 1.99s (flat, cum) 161.79% of Total . . 2: . . 3:import ( . . 4: "testing" . . 5:) . . 6: 540ms 540ms 7:func FibRecursive(n int) int { 40ms 40ms 8: if n < 2 { 200ms 200ms 9: return n . . 10: } 430ms 1.21s 11: return FibRecursive(n-1) + FibRecursive(n-2) . . 12:} . . 13: . . 14:func BenchmarkFibRecursive10(b *testing.B) { . . 15: // run the FibRecursive function b.N times . . 16: for n := 0; n < b.N; n++ {

Show kết quả graph bằng png, pdf dùng command:

(pprof) web

images

Kết thúc phân tích

(pprof) q

Phân tích về cấp phát bộ nhớ

go tool pprof memprofile.out

(pprof) top
Showing nodes accounting for 5140.68kB, 100% of 5140.68kB total
Showing top 10 nodes out of 30 flat flat% sum% cum cum% 1537.69kB 29.91% 29.91% 1537.69kB 29.91% runtime.allocm 1024.41kB 19.93% 49.84% 1024.41kB 19.93% runtime.malg 902.59kB 17.56% 67.40% 1553.21kB 30.21% compress/flate.NewWriter 650.62kB 12.66% 80.05% 650.62kB 12.66% compress/flate.(*compressor).init 512.88kB 9.98% 90.03% 512.88kB 9.98% flag.(*FlagSet).Var 512.50kB 9.97% 100% 512.50kB 9.97% hash/crc32.simpleMakeTable (inline) 0 0% 100% 1553.21kB 30.21% compress/gzip.(*Writer).Write 0 0% 100% 512.88kB 9.98% flag.Var (inline) 0 0% 100% 512.50kB 9.97% hash/crc32.init 0 0% 100% 512.88kB 9.98% main.main

(pprof) web

images

Test benchmarks với fibonaci non recursive

Cài đặt thuật toán với cách tiếp cận là khử đệ quy.

Cài đặt test benchmark với đầu vào N = 10 (giống bước trên)

package bench import ( "testing"
) func FibNonRecursive(n int) int { if n < 2 { return n } f1 := 0 f2 := 1 for i := 2; i < n; i++ { tmp := f2 f2 = f1 + f2 f1 = tmp } return f2
} func BenchmarkFibNonRecursive10(b *testing.B) { // run the FibNonRecursive function b.N times for n := 0; n < b.N; n++ { FibNonRecursive(10) }
}

Optimized

Do biến tmp được cấp phát trong vòng lặp làm tăng bộ nhớ khá nhiều, gây nên kết luận sai. Sau khi tìm hiểu được nguyên nhân thì mình sửa thành như sau

package bench import ( "testing"
) func FibNonRecursive(n int) int { if n < 2 { return n } f1 := 0 f2 := 1 tmp := 0 for i := 2; i < n; i++ { tmp = f2 f2 = f1 + f2 f1 = tmp } return f2
} func BenchmarkFibNonRecursive10(b *testing.B) { // run the FibNonRecursive function b.N times for n := 0; n < b.N; n++ { FibNonRecursive(10) }
}

Phân tích

Tiếp tục chạy lệnh tương tự

go test -bench=. -benchmem -memprofile memprofile.out -cpuprofile profile.out

Kết quả:

goos: darwin
goarch: amd64
pkg: github.com/hblab-ngocnd/csv-demo/bench
cpu: Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz
BenchmarkFibNonRecursive10-12 223505781 5.321 ns/op 0 B/op 0 allocs/op
PASS
ok github.com/hblab-ngocnd/csv-demo/bench 4.895s

223505781 phép toán mỗi phép toán mất 5.321 ns tổng 1189274260.7 ns

Optimized

goos: darwin
goarch: amd64
pkg: github.com/hblab-ngocnd/csv-demo/bench
cpu: Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz
BenchmarkFibNonRecursive10-12 238679884 4.987 ns/op 0 B/op 0 allocs/op
PASS
ok github.com/hblab-ngocnd/csv-demo/bench 1.936s

Chạy lệnh

go tool pprof profile.out

(pprof) top

Type: cpu
Time: Aug 25, 2022 at 3:43pm (+07)
Duration: 1.85s, Total samples = 1.48s (79.92%)
Entering interactive mode (type "help" for commands, "o" for options)
(pprof) top
Showing nodes accounting for 1480ms, 100% of 1480ms total flat flat% sum% cum cum% 980ms 66.22% 66.22% 980ms 66.22% github.com/hblab-ngocnd/csv-demo/bench.FibNonRecursive (inline) 360ms 24.32% 90.54% 1460ms 98.65% github.com/hblab-ngocnd/csv-demo/bench.BenchmarkFibNonRecursive10 120ms 8.11% 98.65% 120ms 8.11% runtime.asyncPreempt 20ms 1.35% 100% 20ms 1.35% runtime.nanotime1 0 0% 100% 20ms 1.35% runtime.nanotime 0 0% 100% 20ms 1.35% testing.(*B).StopTimer 0 0% 100% 1480ms 100% testing.(*B).launch 0 0% 100% 1480ms 100% testing.(*B).runN 0 0% 100% 20ms 1.35% time.Since

Mất tổng thời gian chạy cho FibNonRecursive980ms

Optimized

Type: cpu
Time: Aug 26, 2022 at 3:20am (+07)
Duration: 1.85s, Total samples = 1.51s (81.53%)
Entering interactive mode (type "help" for commands, "o" for options)
(pprof) top
Showing nodes accounting for 1.51s, 100% of 1.51s total
Showing top 10 nodes out of 20 flat flat% sum% cum cum% 0.90s 59.60% 59.60% 0.90s 59.60% github.com/hblab-ngocnd/csv-demo/bench.FibNonRecursive (inline) 0.55s 36.42% 96.03% 1.47s 97.35% github.com/hblab-ngocnd/csv-demo/bench.BenchmarkFibNonRecursive10 0.02s 1.32% 97.35% 0.02s 1.32% runtime.asyncPreempt 0.02s 1.32% 98.68% 0.02s 1.32% runtime.libcCall 0.01s 0.66% 99.34% 0.02s 1.32% runtime.kevent 0.01s 0.66% 100% 0.02s 1.32% runtime.pthread_cond_wait 0 0% 100% 0.03s 1.99% runtime.findrunnable 0 0% 100% 0.02s 1.32% runtime.mPark (inline) 0 0% 100% 0.03s 1.99% runtime.mcall 0 0% 100% 0.02s 1.32% runtime.netpoll

Mất tổng thời gian chạy cho FibNonRecursive0.90s

(pprof) list FibNonRecursive

(pprof) list FibNonRecursive
Total: 1.48s
ROUTINE ======================== github.com/hblab-ngocnd/csv-demo/bench.BenchmarkFibNonRecursive10 in /Users/S16593/Hblab/Motify/csv-demo/bench/Figo_test.go 360ms 1.46s (flat, cum) 98.65% of Total . . 32: return f2 . . 33:} . . 34: . . 35:func BenchmarkFibNonRecursive10(b *testing.B) { . . 36: // run the FibNonRecursive function b.N times 110ms 160ms 37: for n := 0; n < b.N; n++ { 250ms 1.23s 38: FibNonRecursive(10) . . 39: } . 70ms 40:}
ROUTINE ======================== github.com/hblab-ngocnd/csv-demo/bench.FibNonRecursive in /Users/S16593/Hblab/Motify/csv-demo/bench/Figo_test.go 980ms 980ms (flat, cum) 66.22% of Total . . 22: if n < 2 { . . 23: return n . . 24: } . . 25: f1 := 0 . . 26: f2 := 1 980ms 980ms 27: for i := 2; i < n; i++ { . . 28: tmp := f2 . . 29: f2 = f1 + f2 . . 30: f1 = tmp . . 31: } . . 32: return f2

Optimized

(pprof) list FibNonRecursive
Total: 1.51s
ROUTINE ======================== github.com/hblab-ngocnd/csv-demo/bench.BenchmarkFibNonRecursive10 in /Users/S16593/Hblab/Motify/csv-demo/bench/Figo_test.go 550ms 1.47s (flat, cum) 97.35% of Total . . 33: return f2 . . 34:} . . 35: . . 36:func BenchmarkFibNonRecursive10(b *testing.B) { . . 37: // run the FibNonRecursive function b.N times 190ms 200ms 38: for n := 0; n < b.N; n++ { 360ms 1.26s 39: FibNonRecursive(10) . . 40: } . 10ms 41:}
ROUTINE ======================== github.com/hblab-ngocnd/csv-demo/bench.FibNonRecursive in /Users/S16593/Hblab/Motify/csv-demo/bench/Figo_test.go 900ms 900ms (flat, cum) 59.60% of Total . . 23: return n . . 24: } . . 25: f1 := 0 . . 26: f2 := 1 . . 27: tmp := f2 900ms 900ms 28: for i := 2; i < n; i++ { . . 29: tmp = f2 . . 30: f2 = f1 + f2 . . 31: f1 = tmp . . 32: } . . 33: return f2

(pprof) list web

images

Optimized

images

(pprof) q

Phân tích về cấp phát bộ nhớ

go tool pprof memprofile.out

(pprof) top

Type: alloc_space
Time: Aug 25, 2022 at 3:43pm (+07)
Entering interactive mode (type "help" for commands, "o" for options)
(pprof) top
Showing nodes accounting for 5811.98kB, 100% of 5811.98kB total
Showing top 10 nodes out of 26 flat flat% sum% cum cum% 2050.25kB 35.28% 35.28% 2050.25kB 35.28% runtime.allocm 1184.27kB 20.38% 55.65% 1184.27kB 20.38% runtime/pprof.StartCPUProfile 902.59kB 15.53% 71.18% 1553.21kB 26.72% compress/flate.NewWriter 650.62kB 11.19% 82.38% 650.62kB 11.19% compress/flate.(*compressor).init 512.20kB 8.81% 91.19% 512.20kB 8.81% runtime.malg 512.05kB 8.81% 100% 1696.32kB 29.19% runtime.main 0 0% 100% 1553.21kB 26.72% compress/gzip.(*Writer).Write 0 0% 100% 1184.27kB 20.38% main.main 0 0% 100% 1025.12kB 17.64% runtime.mcall 0 0% 100% 1025.12kB 17.64% runtime.mstart

Optimized

Type: alloc_space
Time: Aug 26, 2022 at 3:20am (+07)
Entering interactive mode (type "help" for commands, "o" for options)
(pprof) top
Showing nodes accounting for 3746.37kB, 100% of 3746.37kB total
Showing top 10 nodes out of 21 flat flat% sum% cum cum% 1537.69kB 41.04% 41.04% 1537.69kB 41.04% runtime.allocm 1184.27kB 31.61% 72.66% 1184.27kB 31.61% runtime/pprof.StartCPUProfile 1024.41kB 27.34% 100% 1024.41kB 27.34% runtime.malg 0 0% 100% 1184.27kB 31.61% main.main 0 0% 100% 1184.27kB 31.61% runtime.main 0 0% 100% 512.56kB 13.68% runtime.mcall 0 0% 100% 1025.12kB 27.36% runtime.mstart 0 0% 100% 1025.12kB 27.36% runtime.mstart0 0 0% 100% 1025.12kB 27.36% runtime.mstart1 0 0% 100% 1537.69kB 41.04% runtime.newm

(pprof) web

images

Optimized

images

So sánh và kết luận với trường hợp N = 10

func BenchmarkFib10(b *testing.B) { // run the FibRecursive function b.N times for n := 0; n < b.N; n++ { FibRecursive(10) } for n := 0; n < b.N; n++ { FibNonRecursive(10) } for n := 0; n < b.N; n++ { FibNonRecursiveOptimized(10) }
}
Type: cpu
Time: Aug 26, 2022 at 3:55am (+07)
Duration: 1.54s, Total samples = 1.22s (79.30%)
Entering interactive mode (type "help" for commands, "o" for options)
(pprof) top
Showing nodes accounting for 1.22s, 100% of 1.22s total
Showing top 10 nodes out of 18 flat flat% sum% cum cum% 1.15s 94.26% 94.26% 1.16s 95.08% github.com/hblab-ngocnd/csv-demo/bench.FibRecursive 0.02s 1.64% 95.90% 1.21s 99.18% github.com/hblab-ngocnd/csv-demo/bench.BenchmarkFib10 0.02s 1.64% 97.54% 0.02s 1.64% github.com/hblab-ngocnd/csv-demo/bench.FibNonRecursiveOptimized (inline) 0.01s 0.82% 98.36% 0.01s 0.82% github.com/hblab-ngocnd/csv-demo/bench.FibNonRecursive (inline) 0.01s 0.82% 99.18% 0.01s 0.82% runtime.(*spanSet).push 0.01s 0.82% 100% 0.01s 0.82% runtime.asyncPreempt 0 0% 100% 0.01s 0.82% runtime.(*mcache).releaseAll 0 0% 100% 0.01s 0.82% runtime.(*mcentral).uncacheSpan 0 0% 100% 0.01s 0.82% runtime.ReadMemStats 0 0% 100% 0.01s 0.82% runtime.ReadMemStats.func1

images

Tiêu chí Recursive Non Recursive Non Recursive Optimized
Time 1.16s 0.01s 0.02s
Memory 5140.68kB 5811.98kB 3746.37kB

Lưu ý : Tiêu chí về memory so sánh là chưa hợp lý

Updated

Đúng với nhận xét ban đầu, sau khi optimized Non Recursive có thời gian chạy thấp hơn và tốn ít bộ nhớ hơn so với Recursive

Bạn hãy test thử xem let try !!!

Tài liệu tham khảo

Happy codding !!!

Bình luận

Bài viết tương tự

- vừa được xem lúc

gRPC - Nó là gì và có nên sử dụng hay không?

Nhân một ngày rảnh rỗi, mình ngồi đọc lại RPC cũng như gRPC viết lại để nhớ lâu hơn. Vấn đề là gì và tại sao cần nó .

0 0 132

- vừa được xem lúc

Embedded Template in Go

Getting Start. Part of developing a web application usually revolves around working with HTML as user interface.

0 0 57

- vừa được xem lúc

Tạo Resful API đơn giản với Echo framework và MySQL

1. Giới thiệu.

0 0 61

- vừa được xem lúc

Sử dụng goquery trong golang để crawler thông tin các website Việt Nam bị deface trên mirror-h.org

. Trong bài viết này, mình sẽ cùng mọi người khám phá một package thu thập dữ liệu có tên là goquery của golang. Mục tiêu chính của chương trình crawler này sẽ là lấy thông tin các website Việt Nam bị deface (là tấn công, phá hoại website, làm thay đổi giao diện hiển thị của một trang web, khi người

0 0 237

- vừa được xem lúc

Tạo ứng dụng craw dữ liệu bing với Golang, Mysql driver

Chào mọi người . Lâu lâu ta lại gặp nhau 1 lần, để tiếp tục series chia sẻ kiến thức về tech, hôm nay mình sẽ tìm hiểu và chia sẻ về 1 ngôn ngữ đang khá hot trong cộng đồng IT đó là Golang.

0 0 76

- vừa được xem lúc

Golang: Rest api and routing using MUX

Routing with MUX. Let's create a simple CRUD api for a blog site. # All . GET articles/ .

0 0 55