Skip to content

Instantly share code, notes, and snippets.

@kougazhang
Last active February 17, 2022 08:33
Show Gist options
  • Save kougazhang/d64ffd8fba3d139192a5e3e85752c680 to your computer and use it in GitHub Desktop.
Save kougazhang/d64ffd8fba3d139192a5e3e85752c680 to your computer and use it in GitHub Desktop.
#golang #benchmark
package transform
import (
"fmt"
"sync"
"testing"
)
func TestSliceRest(t *testing.T) {
arr := make([]int, 0, 100)
arr = append(arr, 1, 2, 3)
fmt.Println(arr)
arr = arr[:0]
fmt.Println(arr) // []
}
// 无slice:
// Benchmark_string 240 4950748 ns/op 164018 B/op 5 allocs/op
func Benchmark_string(b *testing.B) {
for i := 0; i < b.N; i++ {
sendChanStr()
}
}
// 使用 arr:
// Benchmark_arr 2265 519549 ns/op 163987 B/op 4 allocs/op
func Benchmark_arr(b *testing.B) {
for i := 0; i < b.N; i++ {
sendChanArr()
}
}
// 使用 slice
// Benchmark_slice 2227 530600 ns/op 163987 B/op 4 allocs/op
func Benchmark_slice(b *testing.B) {
for i := 0; i < b.N; i++ {
sendChanSlice()
}
}
// 发送数组时重置的几种方法
func TestNewParser2BatchRes3(t *testing.T) {
sendChan4() // 错误写法, slice 引用的是 arr 的地址, arr 重新赋值后 arr 地址不会变
sendChan5() // 正确写法, channel 发送 array
sendChan6() // 错误写法, channel 发送 slice, 使用重新切片重置数组
sendChan7() // 正确写法, channel 发送 slice, 使用make重置数组
}
//[0 1 2 3 4 5 6 7 8 9]
//[10 11 12 13 14 15 16 17 18 19]
//[20 21 22 23 24 25 26 27 28 29]
//[30 31 32 33 34 35 36 37 38 39]
//[40 41 42 43 44 45 46 47 48 49]
//[50 51 52 53 54 55 56 57 58 59]
//[60 61 62 63 64 65 66 67 68 69]
//[70 71 72 73 74 75 76 77 78 79]
//[80 81 82 83 84 85 86 87 88 89]
//[90 91 92 93 94 95 96 97 98 99]
func sendChan7() {
recv := make(chan []int, 100)
var wg sync.WaitGroup
go func() {
for d := range recv {
fmt.Println(d)
}
wg.Done()
}()
wg.Add(1)
arr := make([]int, 0, 10)
var counter int
fmt.Printf("raw %p\n", &arr)
for i := 0; i < 100; i++ {
arr = append(arr, i)
counter++
if counter == 10 {
recv <- arr
counter = 0
arr = make([]int, 0, 10)
}
}
close(recv)
wg.Wait()
}
//[90 91 92 93 94 95 96 97 98 99]
//[90 91 92 93 94 95 96 97 98 99]
//[90 91 92 93 94 95 96 97 98 99]
//[90 91 92 93 94 95 96 97 98 99]
//[90 91 92 93 94 95 96 97 98 99]
//[90 91 92 93 94 95 96 97 98 99]
//[90 91 92 93 94 95 96 97 98 99]
//[90 91 92 93 94 95 96 97 98 99]
//[90 91 92 93 94 95 96 97 98 99]
//[90 91 92 93 94 95 96 97 98 99]
func sendChan6() {
recv := make(chan []int, 100)
var wg sync.WaitGroup
go func() {
for d := range recv {
fmt.Println(d)
}
wg.Done()
}()
wg.Add(1)
arr := make([]int, 0, 10)
var counter int
fmt.Printf("raw %p\n", &arr)
for i := 0; i < 100; i++ {
arr = append(arr, i)
counter++
if counter == 10 {
recv <- arr
counter = 0
arr = arr[:0]
}
}
close(recv)
wg.Wait()
}
//[0 1 2 3 4 5 6 7 8 9]
//[10 11 12 13 14 15 16 17 18 19]
//[20 21 22 23 24 25 26 27 28 29]
//[30 31 32 33 34 35 36 37 38 39]
//[40 41 42 43 44 45 46 47 48 49]
//[50 51 52 53 54 55 56 57 58 59]
//[60 61 62 63 64 65 66 67 68 69]
//[70 71 72 73 74 75 76 77 78 79]
//[80 81 82 83 84 85 86 87 88 89]
//[90 91 92 93 94 95 96 97 98 99]
func sendChan5() {
recv := make(chan [10]int, 100)
var wg sync.WaitGroup
go func() {
for d := range recv {
fmt.Println(d)
}
wg.Done()
}()
wg.Add(1)
var arr [10]int
var counter int
fmt.Printf("raw %p\n", &arr)
for i := 0; i < 100; i++ {
arr[counter] = i
counter++
if counter == 10 {
recv <- arr
counter = 0
arr = [10]int{}
}
}
close(recv)
wg.Wait()
}
// 错误写法输出
//[99 0 0 0 0 0 0 0 0 0]
//[99 0 0 0 0 0 0 0 0 0]
//[99 0 0 0 0 0 0 0 0 0]
//[99 0 0 0 0 0 0 0 0 0]
//[99 0 0 0 0 0 0 0 0 0]
//[99 0 0 0 0 0 0 0 0 0]
//[99 0 0 0 0 0 0 0 0 0]
//[99 0 0 0 0 0 0 0 0 0]
//[99 0 0 0 0 0 0 0 0 0]
//[99 0 0 0 0 0 0 0 0 0]
//[99 0 0 0 0 0 0 0 0 0]
func sendChan4() {
recv := make(chan []int, 100)
var wg sync.WaitGroup
go func() {
for d := range recv {
fmt.Println(d)
}
wg.Done()
}()
wg.Add(1)
var arr [10]int
var counter int
fmt.Printf("raw %p\n", &arr)
for i := 0; i < 100; i++ {
arr[counter] = i
counter++
if counter == 9 {
recv <- arr[:] // slice 引用的是 arr 的地址
counter = 0
arr = [10]int{} // arr 的地址不会变
}
}
close(recv)
wg.Wait()
}
func sendChanSlice() {
line := `xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx`
recv := make(chan [100]string, 100)
var wg sync.WaitGroup
go func() {
for d := range recv {
for range d {
}
}
wg.Done()
}()
wg.Add(1)
var arr [100]string
var counter int
for i := 0; i < 100000; i++ {
arr[counter] = line
counter++
if counter == 100 {
recv <- arr
counter = 0
arr = [100]string{}
}
}
close(recv)
wg.Wait()
}
func sendChanArr() {
line := `xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx`
recv := make(chan [100]string, 100)
var wg sync.WaitGroup
go func() {
for d := range recv {
for range d {
}
}
wg.Done()
}()
wg.Add(1)
var arr [100]string
var counter int
for i := 0; i < 100000; i++ {
arr[counter] = line
counter++
if counter == 100 {
recv <- arr
counter = 0
arr = [100]string{}
}
}
close(recv)
wg.Wait()
}
func sendChanStr() {
line := `xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx`
recv := make(chan string, 10000)
var wg sync.WaitGroup
var counter int
go func() {
for range recv {
counter++
}
wg.Done()
}()
wg.Add(1)
for i := 0; i < 100000; i++ {
recv <- line
}
close(recv)
wg.Wait()
}
@kougazhang
Copy link
Author

kougazhang commented Feb 17, 2022

  • 使用 channel []stringchannel string 速度要快 10 倍
  • slice 发送到 channel 并不会对 slice 中的所有数据进行拷贝,只是拷贝的 slice 的指针地址,形成一个新的地址,所以不要在 2 个 goroutine 直接修改 slice, 当 slice 作为参数时不要修改 slice,参见:https://forum.golangbridge.org/t/send-slice-to-chan/5902

Slices are in essence references to a backing array plus a couple of lengths, and this reference is what is passed around when you give someone a slice. This is the same whether it’s passed as a function parameter, over a channel, etc.

https://blog.golang.org/go-slices-usage-and-internals 288

This means the channel send is cheap. It also means that even though the slice has been sent over a channel you need to take care not to do concurrent modifications of it’s content in the two goroutines.

@kougazhang
Copy link
Author

kougazhang commented Feb 17, 2022

切片是对数组的引用,使用 [:] 时底层指向的数组仍然是相同的。

数组是一段连续的地址空间,重复使用可以节约内存,详见:使用 slice 的用法, https://blog.thinkeridea.com/201901/go/slice_de_yi_xie_shi_yong_ji_qiao.html 这篇文章提到的技巧非常非常强!

@kougazhang
Copy link
Author

使用 copy 释放数组:

参见:https://geektutu.com/post/hpg-slice.html

func lastNumsBySlice(origin []int) []int {
	return origin[len(origin)-2:]
}

func lastNumsByCopy(origin []int) []int {
	result := make([]int, 2)
	copy(result, origin[len(origin)-2:])
	return result
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment