Go 基准测试问题

问题描述 投票:0回答:1

我正在尝试对自定义哈希图实现进行基准测试 -> 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))
    }
}

问题是,如果我尝试改变为测试准备数据的方式(第一个循环),基准测试结果就会改变。这让我相信用于准备数据的内存分配包含在基准测试结果中。 如何从基准测试结果中排除数据准备时间和内存分配?

go hashmap benchmarking
1个回答
1
投票

首先,您需要可重复的结果。创建确定性伪随机数生成器:

    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 的随机密钥将产生几乎唯一的密钥”,因此您可能不会对您认为所做的进行基准测试。

© www.soinside.com 2019 - 2024. All rights reserved.