当前观察:三种Golang数组拷贝方式及性能分析详解
目录
测试测试代码测试结果原理分析copyappend总结在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数组拷贝内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
X 关闭
X 关闭
- 1联想拯救者Y70发布最新预告:售价2970元起 迄今最便宜的骁龙8+旗舰
- 2亚马逊开始大规模推广掌纹支付技术 顾客可使用“挥手付”结账
- 3现代和起亚上半年出口20万辆新能源汽车同比增长30.6%
- 4如何让居民5分钟使用到各种设施?沙特“线性城市”来了
- 5AMD实现连续8个季度的增长 季度营收首次突破60亿美元利润更是翻倍
- 6转转集团发布2022年二季度手机行情报告:二手市场“飘香”
- 7充电宝100Wh等于多少毫安?铁路旅客禁止、限制携带和托运物品目录
- 8好消息!京东与腾讯续签三年战略合作协议 加强技术创新与供应链服务
- 9名创优品拟通过香港IPO全球发售4100万股 全球发售所得款项有什么用处?
- 10亚马逊云科技成立量子网络中心致力解决量子计算领域的挑战