三种Golang数组拷贝方式及性能分析详解
作者:jiaxwu 时间:2023-07-13 07:54:27
在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))
来源:https://juejin.cn/post/7134957581801357348
标签:Golang,数组,拷贝
0
投稿
猜你喜欢
分享10个免费超棒的编程用等宽字体
2023-12-06 11:28:32
Python数据库sqlite3图文实例详解
2024-01-15 07:50:33
Python实现免费音乐下载器
2023-12-26 23:51:16
uni-app入门页面布局之window和tabbar详解
2024-04-08 10:53:33
MySQL切分查询用法分析
2024-01-28 01:11:35
MySQL安装失败的原因及解决步骤
2024-01-17 18:22:27
jupyter notebook更换皮肤主题的实现
2023-04-13 07:31:38
python抖音表白程序源代码
2021-01-29 17:12:38
Python版微信红包分配算法
2022-04-10 12:48:22
python实现挑选出来100以内的质数
2023-03-03 16:32:04
修改python plot折线图的坐标轴刻度方法
2021-06-24 09:09:43
ASP获取网页内容(解决乱码问题)
2009-07-26 10:44:00
python使用cPickle模块序列化实例
2022-06-26 22:35:09
MySQL 使用SQL语句修改表名的实现
2024-01-17 09:14:31
python 实现全球IP归属地查询工具
2023-10-05 16:31:33
golang中包无法引入问题解决
2023-09-03 12:34:58
详细解读Python字符串的使用与f-string
2023-06-29 07:24:21
Python免费试用最新Openai API的步骤
2022-04-08 09:03:03
js 禁用只读文本框获得焦点时的退格键
2024-04-19 10:25:41
Python实现有趣的亲戚关系计算器
2022-02-25 01:11:09