网络编程
位置:首页>> 网络编程>> Go语言>> 三种Golang数组拷贝方式及性能分析详解

三种Golang数组拷贝方式及性能分析详解

作者:jiaxwu  发布时间:2023-07-13 07:54:27 

标签:Golang,数组,拷贝

在Go语言中,我们可以使用forappend()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))

来源:https://juejin.cn/post/7134957581801357348

0
投稿

猜你喜欢

手机版 网络编程 asp之家 www.aspxhome.com