三種Golang數(shù)組拷貝方式及性能分析詳解
在Go語言中,我們可以使用for、append()和copy()進(jìn)行數(shù)組拷貝,對于某些對性能比較敏感且數(shù)組拷貝比較多的場景,我們可以會對拷貝性能比較關(guān)注,這篇文件主要是對比一下這三種方式的性能。
測試
測試條件是把一個64KB的字節(jié)數(shù)組分為64個塊進(jìn)行復(fù)制。
測試代碼
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)
}
}
}測試結(jié)果
可以看到copy的性能是最好的,當(dāng)然append的性能也接近copy,for性能較差。
BenchmarkFori-8 19831 52749 ns/op
BenchmarkAppend-8 775945 1478 ns/op
BenchmarkCopy-8 815556 1473 ns/op
原理分析
我們簡單分析copy和append的原理。
copy
可以看到最終都會調(diào)用memmove()整塊拷貝內(nèi)存,而且是用匯編實現(xiàn)的,因此性能是最好的。
// 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最終會被編譯期轉(zhuǎn)換成以下代碼,也是調(diào)用了memmove()整塊拷貝內(nèi)存,因此其實性能是和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))
總結(jié)
| 拷貝方式 | 性能 | 適合場景 |
|---|---|---|
| for | 較差 | 無法使用append和copy的場景,比如類型不同,需要更加復(fù)雜的判斷等 |
| copy | 好 | 適合提前已經(jīng)分配數(shù)組容量,且不是尾部追加的方式 |
| append | 好 | 適合大多數(shù)情況,尾部追加 |
大部分情況下還是建議使用append,不僅性能好,動態(tài)擴展容量,而且代碼看起來更加清晰!
到此這篇關(guān)于三種Golang數(shù)組拷貝方式及性能分析詳解的文章就介紹到這了,更多相關(guān)Golang數(shù)組拷貝內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
使用golang實現(xiàn)在屏幕上打印進(jìn)度條的操作
這篇文章主要介紹了使用golang實現(xiàn)在屏幕上打印進(jìn)度條的操作,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-03-03
Go中g(shù)in框架的*gin.Context參數(shù)常見實用方法
*gin.Context是處理HTTP請求的核心,ctx代表"context"(上下文),它包含了處理請求所需的所有信息和方法,例如請求數(shù)據(jù)、響應(yīng)構(gòu)建器、路由參數(shù)等,這篇文章主要介紹了Go中g(shù)in框架的*gin.Context參數(shù)常見實用方法,需要的朋友可以參考下2024-07-07
go?module化?import?調(diào)用本地模塊?tidy的方法
這篇文章主要介紹了go?module化?import?調(diào)用本地模塊?tidy的相關(guān)知識,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-09-09

