Sunday, February 6, 2022

[Golang] Pointer Performance 指標傳遞性能

Pointer Performance 

我在O`REILLY出的一本書 "Go學習手冊"內有看到一個很有趣的段落: 指標傳遞性能 ( Pointer Performance )

我把這段的重點直接整理下來(因為很重要) 如下:

  • 無論資料的大小為何,將指標傳入函式的時間都是固定的,大約是1 nanosecond。
  • 如果struct夠大,將struct的指標當成輸入參數或是回傳值可以改善性能
  • 回傳指標 vs. 回傳值的行為比較有意思。如果資料結構小於1MB,回傳指標型別其實比回傳值更慢。例如: 100-byte的資料結構大約要花10 nanosecond 來回傳,但是那個資料結構的指標大約要花30 nanosecond。當資料結構超過1MB,性能優勢就會逆轉,回傳10MB的資料大約要花2 millisecond,但是回傳它的指標大約是0.5millisecond再多一些。
另外,當你從函式回傳值時,你應該優先使用值型別,不要回傳指標型,除非在資料型裡面有狀態需要更改。所以,換句話說,與其將一個指向它的指標傳給函式,不如讓函式實例化struct並回傳它。

Why? 因為指標的背後有很複雜的資料流 ( 實例化struct 會存放在 Heap Memory 並且 Golang 會為記憶體回收行程創造額外的工作。

這本書的作者有提供 Benchmark Test 測試程式: 
https://github.com/learning-go-book/pointer_performance

下列結果是在我的Windows10上用VirtualBox起一個Ubuntu 21.04的虛擬機 ( 1 core + 2048MB )所執行測試後的結果:

danny@ubuntu21-danny:~/git/pointer_performance$ go test -v -bench=. -run=none .
goos: linux
goarch: amd64
pkg: pointer_value_param_perf
cpu: AMD Ryzen 7 5800U with Radeon Graphics
BenchmarkPointer10In
BenchmarkPointer10In            1000000000               1.185 ns/op
BenchmarkValue10In
BenchmarkValue10In              759692007                1.575 ns/op
BenchmarkPointer10Out
BenchmarkPointer10Out           84897354                13.06 ns/op
BenchmarkValue10Out
BenchmarkValue10Out             195042046                6.175 ns/op
BenchmarkPointer100In
BenchmarkPointer100In           874474899                1.375 ns/op
BenchmarkValue100In
BenchmarkValue100In             399738804                3.009 ns/op
BenchmarkPointer100Out
BenchmarkPointer100Out          57985454                18.68 ns/op
BenchmarkValue100Out
BenchmarkValue100Out            141442026                8.496 ns/op
BenchmarkPointer1_000In
BenchmarkPointer1_000In         871008984                1.377 ns/op
BenchmarkValue1_000In
BenchmarkValue1_000In           84154417                14.29 ns/op
BenchmarkPointer1_000Out
BenchmarkPointer1_000Out        19870842                58.50 ns/op
BenchmarkValue1_000Out
BenchmarkValue1_000Out          28082719                42.77 ns/op
BenchmarkPointer100_000In
BenchmarkPointer100_000In       867662985                1.398 ns/op
BenchmarkValue100_000In
BenchmarkValue100_000In           733471              1660 ns/op
BenchmarkPointer100_000Out
BenchmarkPointer100_000Out        346294              3788 ns/op
BenchmarkValue100_000Out
BenchmarkValue100_000Out          348529              3371 ns/op
BenchmarkPointer1_000_000In
BenchmarkPointer1_000_000In     864200361                1.395 ns/op
BenchmarkValue1_000_000In
BenchmarkValue1_000_000In          56034             24777 ns/op
BenchmarkPointer1_000_000Out
BenchmarkPointer1_000_000Out       45961             28100 ns/op
BenchmarkValue1_000_000Out
BenchmarkValue1_000_000Out         20671             59030 ns/op
BenchmarkPointer10_000_000In
BenchmarkPointer10_000_000In    852951956                1.390 ns/op
BenchmarkValue10_000_000In
BenchmarkValue10_000_000In          1615            732429 ns/op
BenchmarkPointer10_000_000Out
BenchmarkPointer10_000_000Out       4448            229350 ns/op
BenchmarkValue10_000_000Out
BenchmarkValue10_000_000Out          780           1538157 ns/op
PASS
ok      pointer_value_param_perf        32.999s

P.S:

Benchmark 測試是透過 go test 指令加上 -bench=.,這樣才會跑 benchmark 部分,否則預設只有跑測試程式。另外 -run 可以用在跑特定的測試函式,但是假設沒有指定 -run 時,會預設跑測試 + Benchmark。這邊補上 -run=none 的用意是不要跑任何測試,只跑 Benchmark

No comments: