golang slice相关常见的性能优化手段

简介: 【10月更文挑战第23天】本文介绍了 Go 语言中切片使用的四个优化技巧:预分配容量、减少中间切片的创建、利用切片的复用特性和合理使用 `copy` 函数。通过这些方法,可以有效提高程序性能,减少不必要的内存分配和数据复制操作。每个技巧都附有详细的原理说明和代码示例,帮助开发者更好地理解和应用。
  1. 预分配容量
  • 原理:在 Go 语言中,切片(Slice)是一种动态数组。当向切片中添加元素时,如果切片的容量不足,Go 会自动重新分配内存,这会涉及到数据的复制和内存的重新分配过程,从而影响性能。预分配容量可以避免这种频繁的内存重新分配。
  • 示例
  • 假设我们要创建一个切片来存储 1000 个整数。不进行预分配容量的情况如下:


func withoutPreAlloc() []int {
           var s []int
           for i := 0; i < 1000; i++ {
               s = append(s, i)
           }
           return s
       }


  • 进行预分配容量的情况:


func withPreAlloc() []int {
           s := make([]int, 0, 1000)
           for i := 0; i < 1000; i++ {
               s = append(s, i)
           }
           return s
       }


  • 在性能测试中,可以使用testing包来比较这两种方式的性能差异。例如:


func TestSlicePreAlloc(t *testing.T) {
           t.Run("WithoutPreAlloc", func(t *testing.T) {
               withoutPreAlloc()
           })
           t.Run("WithPreAlloc", func(t *testing.T) {
               withPreAlloc()
           })
       }


  • 通过运行go test -bench=.命令,可以看到预分配容量的方式在处理大量元素添加时通常会更快。


  1. 减少中间切片的创建
  • 原理:每次创建新的切片都会涉及到一定的内存分配和数据复制操作。如果在一个循环或者频繁调用的函数中不断创建中间切片,会导致性能下降。
  • 示例
  • 不好的做法:


func badPractice() []int {
           var result []int
           for i := 0; i < 10; i++ {
               temp := make([]int, i)
               for j := 0; j < i; j++ {
                   temp[j] = j
               }
               result = append(result, temp...)
           }
           return result
       }


  • 改进后的做法:


func goodPractice() []int {
           resultLen := 0
           for i := 0; i < 10; i++ {
               resultLen += i
           }
           result := make([]int, 0, resultLen)
           offset := 0
           for i := 0; i < 10; i++ {
               temp := make([]int, i)
               for j := 0; j < i; j++ {
                   temp[j] = j
               }
               result = append(result, temp...)
               offset += i
           }
           return result
       }


  • 在这个例子中,原做法在每次循环中都创建一个新的临时切片temp,并将其元素追加到result切片中。改进后的做法先计算出最终result切片的总长度,进行预分配容量,然后再将临时切片的元素逐个添加到result切片中,减少了中间切片的创建次数。


  1. 利用切片的复用特性
  • 原理:切片的底层是一个数组,当切片的容量大于长度时,可以对切片进行裁剪,重新利用剩余的容量来存储新的数据,从而避免不必要的内存分配。
  • 示例
  • 比如有一个切片用来缓存数据,当缓存满了之后,我们可以清空切片的部分内容,重新利用它。


func reuseSlice() {
           cache := make([]int, 0, 10)
           for i := 0; i < 10; i++ {
               cache = append(cache, i)
           }
           // 清空切片的前5个元素,重新利用切片
           cache = cache[5:]
           for i := 0; i < 5; i++ {
               cache = append(cache, i + 10)
           }
       }


  • 在这个例子中,先创建了一个容量为 10 的切片并填充数据。然后通过cache = cache[5:]操作,将切片的前 5 个元素丢弃,利用剩下的容量来存储新的数据,这样就避免了重新分配内存来创建新的切片。


  1. 合理使用切片的复制操作(copy函数)
  • 原理:当需要将一个切片的内容复制到另一个切片时,使用copy函数比手动逐个元素复制更高效。copy函数会尽可能地复制元素,直到源切片或目标切片的长度用尽。
  • 示例
  • 假设我们有两个切片,需要将一个切片的内容复制到另一个切片中。


func copySlice() {
           source := []int{1, 2, 3, 4, 5}
           target := make([]int, len(source))
           copy(target, source)
       }


  • 在这里,使用copy函数可以高效地将source切片的内容复制到target切片中。如果不使用copy函数,手动逐个元素复制会更繁琐且可能效率更低。
相关文章
|
5月前
|
存储 Go
Golang底层原理剖析之slice类型与扩容机制
Golang底层原理剖析之slice类型与扩容机制
69 0
|
1月前
|
Go
Golang语言之切片(slice)快速入门篇
这篇文章是关于Go语言中切片(slice)的快速入门教程,详细介绍了切片的概念、定义方式、遍历、扩容机制、使用注意事项以及相关练习题。
28 5
|
4月前
|
Go 索引
GOLANG SLICE 的底层实现
GOLANG SLICE 的底层实现
|
存储 大数据 Go
100天精通Golang(基础入门篇)——第11天:深入解析Go语言中的切片(Slice)及常用函数应用
100天精通Golang(基础入门篇)——第11天:深入解析Go语言中的切片(Slice)及常用函数应用
95 0
|
5月前
|
Go
golang随笔之slice+append的陷阱
golang随笔之slice+append的陷阱
42 0
golang踩坑 1.slice传参和for range赋值
golang踩坑 1.slice传参和for range赋值
|
编译器 Go
golang slice的扩容给你整明白的
golang slice的扩容给你整明白的
|
运维 Go
学习golang(5) 初探:go 数组/slice 的基本使用
学习golang(5) 初探:go 数组/slice 的基本使用
119 0
|
存储 Java Go
【Golang之路】——slice总结
【Golang之路】——slice总结
148 0
【Golang之路】——slice总结