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

golang:如何有效地模拟联合类型

golang:如何有效地模拟联合类型

Go
温温酱 2021-10-25 16:15:21
众所周知,go 没有联合类型,只能通过接口模拟。我尝试了两种方法来模拟联合,但结果远不如C。package mainimport (    "fmt"    "time")type U interface {    i32() int32    i16() int16}type i32 int32func (u i32) i32() int32 {    return int32(u)}func (u i32) i16() int16 {    return int16(u)}type i16 int16func (u i16) i32() int32 {    return int32(u)}func (u i16) i16() int16 {    return int16(u)}func test() (total int64) {    type A struct {        t int32        u interface{}    }    a := [...]A{{1, int32(100)}, {2, int16(3)}}    for i := 0; i < 5000000000; i++ {        p := &a[i%2]        switch p.t {        case 1:            total += int64(p.u.(int32))        case 2:            total += int64(p.u.(int16))        }    }    return}func test2() (total int64) {    type A struct {        t int32        u U    }    a := [...]A{{1, i32(100)}, {2, i16(3)}}    for i := 0; i < 5000000000; i++ {        p := &a[i%2]        switch p.t {        case 1:            total += int64(p.u.i32())        case 2:            total += int64(p.u.i16())        }    }    return}type testfn func() int64func run(f testfn) {    ts := time.Now()    total := f()    te := time.Now()    fmt.Println(total)    fmt.Println(te.Sub(ts))}func main() {    run(test)    run(test2)}结果:2575000000001m23.508223094s25750000000034.95081661s方法方式更好,类型转换方式花费更多的CPU时间。
查看完整描述

3 回答

?
慕妹3146593

TA贡献1820条经验 获得超9个赞

您可以使用数组将单个表示int32为两个int16s,然后按照 Rob Pike 的建议将它们组合起来:


func test3() (total int64) {

    type A struct {

        t int32

        u [2]int16

    }

    a := [...]A{

        {1, [2]int16{100, 0}},

        {2, [2]int16{3, 0}},

    }


    for i := 0; i < N; i++ {

        p := &a[i%2]

        switch p.t {

        case 1:

            total += int64(p.u[0]<<0 | p.u[1]<<8)

        case 2:

            total += int64(p.u[0])

        }

    }

    return

}

使用原始的 Go 编译器,它的运行速度比 C 版本慢约 2 倍,而使用 gccgo (-O3) 时,它的运行速度与 C 一样快。


但请注意,这种方法假定小端整数。您需要切换大端架构的转换顺序。


此外,如果您需要从字节切片解码结构,您应该真正使用encoding/binary. 创建这个库是为了在字节序列和其他类型之间进行转换。


查看完整回答
反对 回复 2021-10-25
?
慕田峪4524236

TA贡献1875条经验 获得超5个赞

联合可能包含数字类型和八位字节字符串,因此我尝试使用字节切片作为值容器并unsafe.Pointer根据具体类型使用它。


func test3() (total int64) {

    type A struct {

        t int32

        u []byte

    }   


    a := [...]A{{1, make([]byte, 8)}, {2, make([]byte, 8)}}

    *(*int32)(unsafe.Pointer(&a[0].u)) = 100 

    *(*int16)(unsafe.Pointer(&a[1].u)) = 3 


    for i := 0; i < 5000000000; i++ {

        p := &a[i%2]

        switch p.t {

        case 1:

            total += int64(*(*int32)(unsafe.Pointer(&p.u)))

        case 2:

            total += int64(*(*int16)(unsafe.Pointer(&p.u)))

        }   

    }   

    return

}

结果:


$ go run union.go

257500000000

12.844752701s


$ go run -compiler gccgo -gccgoflags -O3 union.go

257500000000

6.640667s

它是最好的版本吗?


查看完整回答
反对 回复 2021-10-25
  • 3 回答
  • 0 关注
  • 387 浏览
慕课专栏
更多

添加回答

举报

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