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

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

在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

标签: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
  • asp之家 网络编程 m.aspxhome.com