为了账号安全,请及时绑定邮箱和手机立即绑定

Go按位运算性能之谜

Go按位运算性能之谜

Go
慕码人2483693 2023-03-21 17:21:08
在对从字节数组到 s 的转换性能进行基准测试时uint32,我注意到从最低有效位开始时转换运行得更快:package blahimport (    "testing"    "encoding/binary"    "bytes")func BenchmarkByteConversion(t *testing.B) {    var i uint32 = 3419234848    buf := new(bytes.Buffer)    _ = binary.Write(buf, binary.BigEndian, i)    b := buf.Bytes()    for n := 0; n < t.N; n++ {        // Start with least significant bit: 0.27 nanos        value := uint32(b[3]) | uint32(b[2])<<8 | uint32(b[2])<<16 | uint32(b[0])<<24        // Start with most significant bit: 0.68 nanos        // value := uint32(b[0])<<24 | uint32(b[1])<<16 | uint32(b[2])<<8 | uint32(b[3])        _ = value    }}当我运行时,计算第一种方式go test -bench=.时每次迭代获得 0.27 纳米,计算第二种方式时每次迭代获得 0.68 纳米。为什么将数字放在一起时从最低有效位开始的速度是原来的两倍?valuevalue|
查看完整描述

1 回答

?
幕布斯6054654

TA贡献1876条经验 获得超7个赞

没有什么神秘之处。优化!


package blah


import (

    "bytes"

    "encoding/binary"

    "testing"

)


func BenchmarkByteConversionLeast(t *testing.B) {

    var i uint32 = 3419234848

    buf := new(bytes.Buffer)

    _ = binary.Write(buf, binary.BigEndian, i)

    b := buf.Bytes()


    for n := 0; n < t.N; n++ {

        // Start with least significant bit: 0.27 nanos

        value := uint32(b[3]) | uint32(b[2])<<8 | uint32(b[2])<<16 | uint32(b[0])<<24

        _ = value

    }

}


func BenchmarkByteConversionMost(t *testing.B) {

    var i uint32 = 3419234848

    buf := new(bytes.Buffer)

    _ = binary.Write(buf, binary.BigEndian, i)

    b := buf.Bytes()


    for n := 0; n < t.N; n++ {

        // Start with most significant bit: 0.68 nanos

        value := uint32(b[0])<<24 | uint32(b[1])<<16 | uint32(b[2])<<8 | uint32(b[3])

        _ = value

    }

}

输出:


go test silly_test.go -bench=.

goos: linux

goarch: amd64

BenchmarkByteConversionLeast-4      2000000000           0.72 ns/op

BenchmarkByteConversionMost-4       2000000000           1.80 ns/op

这应该是显而易见的。边界检查消除。


只需使用常识。如果检查索引 3、2、1 和 0 的数组边界,则可以在 3 处停止检查,因为显然 2、1 和 0 也是有效的。如果检查索引 0、1、2 和 3 的数组边界,则必须检查所有索引的边界。一次边界检查与四次边界检查。

维基百科:边界检查

维基百科:边界检查消除


您还应该阅读好的代码,例如 Go 标准库。例如,

func (littleEndian) PutUint64(b []byte, v uint64) {

    _ = b[7] // early bounds check to guarantee safety of writes below

    b[0] = byte(v)

    b[1] = byte(v >> 8)

    b[2] = byte(v >> 16)

    b[3] = byte(v >> 24)

    b[4] = byte(v >> 32)

    b[5] = byte(v >> 40)

    b[6] = byte(v >> 48)

    b[7] = byte(v >> 56)

}


查看完整回答
反对 回复 2023-03-21
  • 1 回答
  • 0 关注
  • 53 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信