wtysos11 / blogWiki

Use to store public paper and organize them.
18 stars 4 forks source link

golang基准测试复现与两种求模方式 #205

Closed wtysos11 closed 3 years ago

wtysos11 commented 3 years ago

今天在读代码的时候遇到了一种比较奇怪的求模方式,顺便以此为基础做一下基准测试(毕竟不是什么时候都有机会写基准测试的)

业务代码部分

package main

import "fmt"

var(
    masks = [65]uint{}
)

func getmodByDefault(x int64) int64{
    return x%(1<<32)
}

func getmodByMask(x int64) int64{
    return x & int64(masks[32])
}

func init(){
    for i := uint(0); i <= 64; i++ {
        masks[i] = (1 << i) - 1
    } //遮盖符号,全部为1,1的数量为i-1
}

//构建一个Benchmark
func main(){
    fmt.Println(getmodByDefault(2000))
    fmt.Println(getmodByMask(2000))
}

我碰到的就是这个mask数组的求模方式了。经过语义和手算我才能确定这确实是求模(毕竟数电学的不太好)。但为什么要这么去计算呢?我开始认为是不是这么做速度会快一些

基准测试

package main

import (
    "math/rand"
    "strconv"
    "testing"
)

func Test_getmodByMask(t *testing.T) {
    type args struct {
        x int64
    }
    tests := []struct {
        name string
        args args
        want int64
    }{}
    for i:=0;i<100;i++{
        targetNum := rand.Int63n(1<<62)
        tests = append(tests, struct {
            name string
            args args
            want int64
        }{name: "test"+strconv.Itoa(i), args: args{targetNum}, want: getmodByDefault(targetNum)})
    }
    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            if got := getmodByMask(tt.args.x); got != tt.want {
                t.Errorf("getmodByMask() = %v, want %v", got, tt.want)
            }
        })
    }
}
func BenchmarkModDefault(b *testing.B) {
    for i:=0;i<b.N;i++{
        _ = getmodByDefault(rand.Int63n(1<<62))
    }
}

func BenchmarkModByMask(b *testing.B) {
    for i:=0;i<b.N;i++{
        _ = getmodByMask(rand.Int63n(1<<62))
    }
}

基准测试的运行需要加上go test -bench=.,不然只会执行单元测试。而且这么执行的话会默认执行单元测试,如果不想要执行单元测试可以go test -run=^$

结果显示两种方式的速度是一样的。这……可能是代码迁移时的区别吧,底层程序员可能会更偏爱位运算之类的?