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

新手上路,请多包涵

众所周知,go 没有联合类型,只能通过接口来模拟。

我尝试了两种方法来模拟联合,但结果远不如C。

 package main

import (
    "fmt"
    "time"
)

type U interface {
    i32() int32
    i16() int16
}

type i32 int32

func (u i32) i32() int32 {
    return int32(u)
}

func (u i32) i16() int16 {
    return int16(u)
}

type i16 int16

func (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() int64

func 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)
}

结果:

 257500000000
1m23.508223094s
257500000000
34.95081661s

method 方式更好,type-cast 方式消耗更多的 CPU 时间。

C版:

 #include <stdio.h>

struct A {
    int t;
    union {
        int i;
        short v;
    } u;
};

long test()
{
    struct A a[2];
    a[0].t = 1;
    a[0].u.i = 100;
    a[1].t = 2;
    a[1].u.v = 3;

    long total = 0;
    long i;
    for (i = 0; i < 5000000000; i++) {
        struct A* p = &a[i % 2];
        switch(p->t) {
        case 1:
            total += p->u.i;
            break;
        case 2:
            total += p->u.v;
            break;
        }
    }
    return total;
}
int main()
{
    long total = test();
    printf("%ld\n", total);
}

结果:

 257500000000

real    0m5.620s
user    0m5.620s
sys 0m0.000s

联合类型对许多应用程序很有用,例如网络协议可能包含变体具体类型。那么联合数据的访问可能会成为应用的瓶颈。

有人可以帮忙吗?谢谢。

原文由 kingluo 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 1.1k
1 个回答

您可以使用数组将单个 int32 表示为两个 int16 s,然后 按照 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 。创建此库是为了在字节序列和其他类型之间进行转换。

原文由 Ainar-G 发布,翻译遵循 CC BY-SA 3.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题