網(wǎng)站首頁 編程語言 正文
在Go語言中,我們可以使用for
、append()
和copy()
進(jìn)行數(shù)組拷貝,對于某些對性能比較敏感且數(shù)組拷貝比較多的場景,我們可以會(huì)對拷貝性能比較關(guān)注,這篇文件主要是對比一下這三種方式的性能。
測試
測試條件是把一個(gè)64KB的字節(jié)數(shù)組分為64個(gè)塊進(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的性能也接近c(diǎn)opy,for性能較差。
BenchmarkFori-8 ? ? ? ? ? ?19831 ? ? ? ? ? ? 52749 ns/op
BenchmarkAppend-8 ? ? ? ? 775945 ? ? ? ? ? ? ?1478 ns/op
BenchmarkCopy-8 ? ? ? ? ? 815556 ? ? ? ? ? ? ?1473 ns/op
原理分析
我們簡單分析copy和append的原理。
copy
代碼
可以看到最終都會(huì)調(diào)用memmove()
整塊拷貝內(nèi)存,而且是用匯編實(shí)現(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最終會(huì)被編譯期轉(zhuǎn)換成以下代碼,也是調(diào)用了memmove()
整塊拷貝內(nèi)存,因此其實(shí)性能是和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,不僅性能好,動(dòng)態(tài)擴(kuò)展容量,而且代碼看起來更加清晰!
原文鏈接:https://juejin.cn/post/7134957581801357348
相關(guān)推薦
- 2022-11-15 python內(nèi)置模塊OS?實(shí)現(xiàn)SHELL端文件處理器_python
- 2022-04-17 docker 執(zhí)行python腳本 并查看日志
- 2022-03-18 .NET?6開發(fā)TodoList應(yīng)用之實(shí)現(xiàn)PUT請求_實(shí)用技巧
- 2023-07-17 uniapp H5頁面內(nèi)獲取手機(jī)號(hào)撥打電話
- 2022-05-20 Python?文件處理之open()函數(shù)_python
- 2022-06-01 Android實(shí)現(xiàn)從相冊選擇照片功能_Android
- 2023-04-18 python去除字符串中的空格、特殊字符和指定字符的三種方法_python
- 2023-03-01 shell?創(chuàng)建子進(jìn)程及并行延時(shí)執(zhí)行命令方法_linux shell
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運(yùn)行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲(chǔ)小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運(yùn)算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯(cuò)誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實(shí)現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡單動(dòng)態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊(duì)列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支