指针接收器在基准测试中并不比值接收器更快。

huangapple go评论87阅读模式
英文:

Pointer receiver not faster than value receiver when benchmarking

问题

这是我正在测试的代码,我原本期望在进行基准测试时,基于指针的addDataPointer函数的性能会比基于值的addData函数更快。为什么两者之间的性能没有明显的变化呢?

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)
	}
}

这段代码定义了一个BigStruct结构体,其中包含一个name字符串和一个data字节切片。addData函数接收一个BigStruct值作为参数,并在data切片的开头添加一些字节。addDataPointer方法接收一个BigStruct指针作为接收者,并在data切片的开头添加一些字节。

main函数中,我们创建了一个BigStructa和一个BigStruct指针b,并分别调用了addData函数和addDataPointer方法。然后打印了它们的结果。

BenchmarkBigLenPointerBenchmarkBigLen是基准测试函数,分别对基于指针和基于值的函数进行性能测试。它们使用testing.B类型的参数来控制测试的次数。

你之所以没有看到两者之间性能的显著变化,可能是因为测试的数据量不够大,或者在你的环境中,基于指针和基于值的函数执行时间相差不大。你可以尝试增加测试数据的大小或者在不同的环境中运行测试来观察性能差异。

英文:

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 &quot;fmt&quot;

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: &quot;greg&quot;, data: []byte(&quot;abc&quot;)}
	b := &amp;BigStruct{name: &quot;greg&quot;, data: []byte(&quot;abc&quot;)}
	fmt.Println(addData(a))
	b.addDataPointer()
	fmt.Println(*b)
}

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

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

答案1

得分: 1

您的基准函数测量的是for循环内部的内容,例如:

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

和:

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

因此,您还对strings.Repeat()进行了基准测试,它会生成一个很长的string值,并将其转换为[]byte,这会复制这个长字符串。

这些操作的执行时间比addDataPointer()方法和addData()函数的执行时间要长得多。

将转换和strings.Repeat()调用移到for循环之外,类似这样:

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)
    }
}

现在,这样更准确地测量了您的方法和函数的时间。但是,即使在对此进行基准测试时,您将得到显示addData()addDataPointer()执行时间非常接近的基准结果。

这是因为在函数的情况下,您传递了一个包含字节切片的结构值。在Go中,切片是小型描述符(类似结构体的头部),它们不包含切片的元素,而只包含指向支持数组的指针。因此,切片值的大小是相同的,无论其长度(元素数量)如何。要查看切片头部的内容,请查看reflect.SliceHeader类型。

因此,函数的开销很小,这可能会对其执行时间产生一点影响,但另一方面,指针方法需要解引用指针,这会增加一点执行时间。最终,它们非常接近。在这种情况下,没有太大的区别。

英文:

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

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

And:

    big := BigStruct{name: &quot;greg&quot;, data: []byte(strings.Repeat(&quot;test1234&quot;, 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(&quot;test1234&quot;, 1024))
    for i := 0; i &lt; b.N; i++ {
        big := &amp;BigStruct{name: &quot;greg&quot;, data: s}
        big.addDataPointer()
    }
}

func BenchmarkBigLen(b *testing.B) {
	s := []byte(strings.Repeat(&quot;test1234&quot;, 1024))
    for i := 0; i &lt; b.N; i++ {
        big := BigStruct{name: &quot;greg&quot;, 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.

huangapple
  • 本文由 发表于 2016年10月30日 19:40:10
  • 转载请务必保留本文链接:https://go.coder-hub.com/40328658.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定