三種Golang數組拷貝方式及性能分析詳解
在Go語言中,我們可以使用for
、append()
和copy()
進行數組拷貝,對於某些對性能比較敏感且數組拷貝比較多的場景,我們可以會對拷貝性能比較關註,這篇文件主要是對比一下這三種方式的性能。
測試
測試條件是把一個64KB的字節數組分為64個塊進行復制。
測試代碼
package test import ( "testing" ) const ( blocks = 64 blockSize = 1024 ) var block = make([]byte, blockSize) func BenchmarkFori(b *testing.B) { a := make([]byte, blocks*blockSize) for n := 0; n < b.N; n++ { for i := 0; i < blocks; i++ { for j := 0; j < blockSize; j++ { a[i*blockSize+j] = block[j] } } } } func BenchmarkAppend(b *testing.B) { a := make([]byte, 0, blocks*blockSize) for n := 0; n < b.N; n++ { a = a[:0] for i := 0; i < blocks; i++ { a = append(a, block...) } } } func BenchmarkCopy(b *testing.B) { a := make([]byte, blocks*blockSize) for n := 0; n < b.N; n++ { for i := 0; i < blocks; i++ { copy(a[i*blockSize:], block) } } }
測試結果
可以看到copy的性能是最好的,當然append的性能也接近copy,for性能較差。
BenchmarkFori-8 19831 52749 ns/op
BenchmarkAppend-8 775945 1478 ns/op
BenchmarkCopy-8 815556 1473 ns/op
原理分析
我們簡單分析copy和append的原理。
copy
代碼
可以看到最終都會調用memmove()
整塊拷貝內存,而且是用匯編實現的,因此性能是最好的。
// slicecopy is used to copy from a string or slice of pointerless elements into a slice. func slicecopy(toPtr unsafe.Pointer, toLen int, fromPtr unsafe.Pointer, fromLen int, width uintptr) int { if fromLen == 0 || toLen == 0 { return 0 } n := fromLen if toLen < n { n = toLen } if width == 0 { return n } size := uintptr(n) * width if raceenabled { callerpc := getcallerpc() pc := funcPC(slicecopy) racereadrangepc(fromPtr, size, callerpc, pc) racewriterangepc(toPtr, size, callerpc, pc) } if msanenabled { msanread(fromPtr, size) msanwrite(toPtr, size) } if size == 1 { // common case worth about 2x to do here // TODO: is this still worth it with new memmove impl? *(*byte)(toPtr) = *(*byte)(fromPtr) // known to be a byte pointer } else { memmove(toPtr, fromPtr, size) } return n }
append
代碼
append最終會被編譯期轉換成以下代碼,也是調用瞭memmove()
整塊拷貝內存,因此其實性能是和copy差不多的。
s := l1 n := len(s) + len(l2) // Compare as uint so growslice can panic on overflow. if uint(n) > uint(cap(s)) { s = growslice(s, n) } s = s[:n] memmove(&s[len(l1)], &l2[0], len(l2)*sizeof(T))
總結
拷貝方式 | 性能 | 適合場景 |
---|---|---|
for | 較差 | 無法使用append和copy的場景,比如類型不同,需要更加復雜的判斷等 |
copy | 好 | 適合提前已經分配數組容量,且不是尾部追加的方式 |
append | 好 | 適合大多數情況,尾部追加 |
大部分情況下還是建議使用append,不僅性能好,動態擴展容量,而且代碼看起來更加清晰!
到此這篇關於三種Golang數組拷貝方式及性能分析詳解的文章就介紹到這瞭,更多相關Golang數組拷貝內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- Go如何優雅的使用字節池示例詳解
- Golang實現AES對稱加密的過程詳解
- Go語言使用對稱加密的示例詳解
- 通過Golang編寫一個AES加密解密工具
- golang中切片copy復制和等號復制的區別介紹