golang中的函数与开关表

pvi*_*nis 6 emulation go gameboy

我正在写一个简单的模拟器(我应该?或者我应该回到c?).无论如何,我正在获取指令并对其进行解码.在这一点上,我有一个像0x81的字节,我必须执行正确的功能.

我应该有这样的东西吗?

func (sys *cpu) eval() {
    switch opcode {
    case 0x80:
        sys.add(sys.b)
    case 0x81:
        sys.add(sys.c)
    etc
    }
}
Run Code Online (Sandbox Code Playgroud)

或类似的东西

var fnTable = []func(*cpu) {
    0x80: func(sys *cpu) {
        sys.add(sys.b)
    },
    0x81: func(sys *cpu) {
        sys.add(sys.c)
    }
}
func (sys *cpu) eval() {
    return fnTable[opcode](sys)
}
Run Code Online (Sandbox Code Playgroud)

哪一个更好?
哪一个更快?
还有
3.我可以声明一个内联函数吗?
4.i有一个cpu struct我有寄存器等,如果我有寄存器和全部作为全局变量会更快吗?(没有struct)

非常感谢你.

Eva*_*haw 15

我做了一些基准测试,一旦你有超过4个案例,表版本比交换机版本更快.

我很惊讶地发现Go编译器(gc,无论如何;不确定gccgo)似乎不够聪明,无法将密集的开关变成跳转表.

更新:Ken Thompson在Go邮件列表上发布了描述优化交换机的困难.

  • 对于足够大的开关,它并不快.较小的交换机可以更快,因为它们能够更好地利用CPU的分支预测器和代码缓存. (2认同)

zzz*_*zzz 3

  1. 第一个版本对我来说看起来更好,YMMV。

  2. 对其进行基准测试。取决于编译器的优化程度。如果编译器没有尽力优化,“跳转表”版本可能会更快。

  3. 取决于您对“声明函数内联”的定义。Go 只能在顶层声明和定义函数/方法。但函数是 Go 中的一等公民,因此可以具有变量/参数/返回值和函数类型的结构化类型。在所有这些地方,函数文字也可以分配给变量/字段/元素......

  4. 可能吧。不过我建议不要将 cpu 状态保存在全局变量中。一旦您决定去模拟多核,我们会很欢迎;-)