我应该有这样的东西
func (sys *cpu) eval() {
switch opcode {
case 0x80:
sys.add(sys.b)
case 0x81:
sys.add(sys.c)
etc
}
}
或类似的东西
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)
}
1.哪一个更好?
哪一个更快?
也
3.我可以声明一个内联函数吗?
4.i有一个cpu
struct
我有寄存器等。如果我有寄存器和全部为全局变量会更快吗? (没有struct
)
答案 0 :(得分:15)
我做了一些基准测试,一旦你有超过4个案例,表格版本比交换机版本更快。
我很惊讶地发现Go编译器(gc,无论如何;不确定gccgo)似乎不够聪明,无法将密集的开关变成跳转表。
<强>更新强>: Ken Thompson在Go邮件列表上发布了the difficulties of optimizing switch。
答案 1 :(得分:2)
对我来说第一个版本看起来更好,YMMV。
基准测试。取决于编译器在优化方面有多好。如果编译器没有努力进行优化,那么“跳转表”版本可能会更快。
取决于您对“声明函数内联”的定义。 Go只能在顶层声明和定义函数/方法。但是函数是Go中的一等公民,因此可以有变量/参数/返回值和结构类型的函数类型。在所有这些地方,[{3}}也可以[也]分配给变量/ field / element ...
可能。我仍然建议不要将cpu状态保存在全局变量中。一旦你决定去模拟多核,就会受到欢迎; - )
答案 2 :(得分:0)
如果你有一些表达式的ast,并且想要为大量数据行评估它,那么你可能只将它编译成lambdas树,并且根本不计算每次迭代的任何开关;
例如,给出这样的:{* (a, {+ (b, c)})}
编译功能(非常粗略的伪语言)将是这样的:
func (e *evaluator) compile(brunch ast) {
switch brunch.type {
case binaryOperator:
switch brunch.op {
case *: return func() {compile(brunch.arg0) * compile(brunch.arg1)}
case +: return func() {compile(brunch.arg0) + compile(brunch.arg1)}
}
case BasicLit: return func() {return brunch.arg0}
case Ident: return func(){return e.GetIdent(brunch.arg0)}
}
}
因此最终编译返回func,必须在数据的每一行调用func,并且根本不会有任何开关或其他计算内容。 关于不同类型数据的操作仍有问题,这是您自己的研究;) 这是一种有趣的方法,在没有跳转表机制可用的情况下:)但是肯定,func调用操作更复杂然后跳转。