基准测试时指针接收器不快于值接收器

Here is the code I'm testing, I was expecting to see that when bench marking, the pointer based addDataPointer would perform faster than the addData value based function. Why is there no significant changes in performance between the two?

package main

import "fmt"

type BigStruct struct {
    name string
    data []byte
}

func addData(s BigStruct) BigStruct {
    s.data = append([]byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05}, s.data...)
    return BigStruct{name: s.name, data: s.data}
}

func (s *BigStruct) addDataPointer() {
    s.data = append([]byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05}, s.data...)
}

func main() {
    a := BigStruct{name: "greg", data: []byte("abc")}
    b := &BigStruct{name: "greg", data: []byte("abc")}
    fmt.Println(addData(a))
    b.addDataPointer()
    fmt.Println(*b)
}

func BenchmarkBigLenPointer(b *testing.B) {
    for i := 0; i < b.N; i++ {
        big := &BigStruct{name: "greg", data: []byte(strings.Repeat("test1234", 1024))}
        big.addDataPointer()
    }
}

func BenchmarkBigLen(b *testing.B) {
    for i := 0; i < b.N; i++ {
        big := BigStruct{name: "greg", data: []byte(strings.Repeat("test1234", 1024))}
        addData(big)
    }

Your benchmark functions measure whatever is inside the for loops, e.g.:

    big := &BigStruct{name: "greg", data: []byte(strings.Repeat("test1234", 1024))}
    big.addDataPointer()

And:

    big := BigStruct{name: "greg", data: []byte(strings.Repeat("test1234", 1024))}
    addData(big)

So you also benchmark strings.Repeat(), which gives you a long string value, and you also convert it to []byte, which makes a copy of this lengthy string.

The execution times of these are much bigger than the execution times of your addDataPointer() method and addData() function.

Move the conversion and strings.Repeat() call outside of the for loops, something like this:

func BenchmarkBigLenPointer(b *testing.B) {
    s := []byte(strings.Repeat("test1234", 1024))
    for i := 0; i < b.N; i++ {
        big := &BigStruct{name: "greg", data: s}
        big.addDataPointer()
    }
}

func BenchmarkBigLen(b *testing.B) {
    s := []byte(strings.Repeat("test1234", 1024))
    for i := 0; i < b.N; i++ {
        big := BigStruct{name: "greg", data: s}
        addData(big)
    }
}

Now this is more accurate to measure the time of your method and function. But even when you benchmark this, you will get benchmark results that show that execution times of both addData() and addDataPointer() are very close.

The explanation for this is that in case of the function you pass a struct value containing a byte slice. Slices in Go are small descriptors (struct like headers), which do not contain the elements of the slices but only a pointer to a backing array. So the size of a slice value is the same, regardless of its length (number of elements). To see what's inside a slice header, check out the reflect.SliceHeader type.

So the overhead of the function is little, which might give a little to its execution time, but the pointer method on the other hand needs to dereference the pointer, which adds a little to its execution time. In the end, they are very close. In this case there is not much difference.