我正在尝试对自定义哈希图实现进行基准测试 -> https://github.com/dmarro89/go-redis-hashtable。 该结构目前有 3 个方法:Set、Get 和 Delete。
在Set Benchmark中,我准备数据,然后调用Set方法,尝试测量执行时间和内存分配。
package main
import (
"fmt"
"testing"
"github.com/dmarro89/go-redis-hashtable/datastr"
"github.com/stretchr/testify/assert"
)
func BenchmarkSet(b *testing.B) {
var d = datastr.NewDict()
insertedElements := make(map[string]interface{})
fmt.Printf("Inserting elements %d times \n", b.N)
for i := 0; i < b.N; i++ {
key := randomString(20)
value := randomString(100)
insertedElements[key] = value
}
b.ResetTimer()
for key, value := range insertedElements {
b.StartTimer()
err := d.Set(key, value)
b.StopTimer()
assert.NoError(b, err, fmt.Sprintf("Error adding element {%s, %+v} to dictionary: %v", key, value, err))
}
}
问题是,如果我尝试改变为测试准备数据的方式(第一个循环),基准测试结果就会改变。这让我相信用于准备数据的内存分配包含在基准测试结果中。 如何从基准测试结果中排除数据准备时间和内存分配?
首先,您需要可重复的结果。创建确定性伪随机数生成器:
rnd := rand.New(rand.NewSource(42)) //nolint:gosec
并使用它:
func randomString(rnd *rand.Rand, n int) string {
p := make([]byte, n)
rnd.Read(p)
return string(p)
}
请注意,
math/rand/v2
可用且通常应使用。我没有费心去举这个例子。
接下来,由于您想要确定性顺序,请将测试数据生成为数组,而不是映射:
type keyValue struct{ Key, Value string }
insertedElements := make([]keyValue, 0, b.N)
rnd := rand.New(rand.NewSource(42)) //nolint:gosec
for range b.N {
key := randomString(rnd, 20)
value := randomString(rnd, 100)
insertedElements = append(insertedElements, keyValue{Key: key, Value: value})
}
现在,设置完成后,重置计时器并执行基准测试:
d := datastr.NewDict()
b.ResetTimer()
for _, kv := range insertedElements {
if err := d.Set(kv.Key, kv.Value); err != nil {
b.Errorf("Error adding element {%v, %v} to dictionary: %v", []byte(kv.Key), []byte(kv.Value), err)
}
}
if
的影响应该可以忽略不计,小于调用停止/启动。尝试不进行错误检查的基准测试来验证。
全部放在一个文件中:
package main
import (
"math/rand"
"testing"
"github.com/dmarro89/go-redis-hashtable/datastr"
)
func BenchmarkSet(b *testing.B) {
type keyValue struct{ Key, Value string }
insertedElements := make([]keyValue, 0, b.N)
rnd := rand.New(rand.NewSource(42)) //nolint:gosec
for range b.N {
key := randomString(rnd, 20)
value := randomString(rnd, 100)
insertedElements = append(insertedElements, keyValue{Key: key, Value: value})
}
d := datastr.NewDict()
b.ResetTimer()
for _, kv := range insertedElements {
if err := d.Set(kv.Key, kv.Value); err != nil {
b.Errorf("Error adding element {%v, %v} to dictionary: %v", []byte(kv.Key), []byte(kv.Value), err)
}
}
}
func randomString(rnd *rand.Rand, n int) string {
p := make([]byte, n)
rnd.Read(p)
return string(p)
}
请注意,@Volker如何正确评论,“插入长度为 20 的随机密钥将产生几乎唯一的密钥”,因此您可能不会对您认为所做的进行基准测试。