英文:
table of functions vs switch in golang
问题
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 fnTableopcode
}
1.哪一个更好?
2.哪一个更快?
还有
3.我可以内联声明一个函数吗?
4.我有一个cpu
结构体,其中包含寄存器等。如果我将寄存器和所有内容作为全局变量,会更快吗?(不使用结构体)
非常感谢。
英文:
im am writing a simple emulator in go (should i? or should i go back to c?).
anyway, i am fetching the instruction and decoding it. at this point i have a byte like 0x81, and i have to execute the right function.
should i have something like this
func (sys *cpu) eval() {
switch opcode {
case 0x80:
sys.add(sys.b)
case 0x81:
sys.add(sys.c)
etc
}
}
or something like this
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.which one is better?
2.which one is faster?
also
3.can i declare a function inline?
4.i have a cpu
struct
in which i have the registers etc. would it be faster if i have the registers and all as globals? (without the struct
)
thank you very much.
答案1
得分: 18
我做了一些基准测试,发现当你有超过大约4个case时,表格版本比开关版本更快。
我很惊讶地发现Go编译器(至少是gc,不确定gccgo)似乎不够聪明,不能将密集的开关转换为跳转表。
更新:
Ken Thompson在Go邮件列表上发表了一篇描述优化开关的困难的帖子。
英文:
I did some benchmarks and the table version is faster than the switch version once you have more than about 4 cases.
I was surprised to discover that the Go compiler (gc, anyway; not sure about gccgo) doesn't seem to be smart enough to turn a dense switch into a jump table.
Update:
Ken Thompson posted on the Go mailing list describing the difficulties of optimizing switch.
答案2
得分: 3
-
第一个版本对我来说看起来更好,但可能因人而异。
-
进行基准测试。这取决于编译器在优化方面的能力。如果编译器没有尽力优化,那么“跳转表”版本可能会更快。
-
这取决于你对“声明内联函数”的定义。Go语言只能在顶层声明和定义函数/方法。但是在Go语言中,函数是一等公民,所以可以有函数类型的变量/参数/返回值和结构化类型。在所有这些地方,都可以将函数字面量分配给变量/字段/元素...
-
可能。不过我建议不要将CPU状态保存在全局变量中。一旦你决定模拟多核,它将会很受欢迎
英文:
-
The first version looks better to me, YMMV.
-
Benchmark it. Depends how good is the compiler at optimizing. The "jump table" version might be faster if the compiler doesn't try hard enough to optimize.
-
Depends on your definition of what is "to declare function inline". Go can declare and define functions/methods at the top level only. But functions are first class citizens in Go, so one can have variables/parameters/return values and structured types of function type. In all this places a function literal can [also] be assigned to the variable/field/element...
-
Possibly. Still I would suggest to not keep the cpu state in a global variable. Once you possibly decide to go emulating multicore, it will be welcome
答案3
得分: 0
如果您有某个表达式的AST,并且想要对大量数据行进行评估,那么您只需要将其编译成lambda树,并且在每次迭代中不进行任何开关计算;
例如,给定这样的AST:{* (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)}
}
}
因此,最终编译返回一个函数,必须在数据的每一行上调用该函数,而且根本不会有开关或其他计算内容。
关于不同类型数据的操作问题仍然存在,这需要您自己进行研究;)
这是一种有趣的方法,在没有跳转表机制可用的情况下:) 但是,确实,函数调用比跳转更复杂。
英文:
If you have the ast of some expression, and you want to eval it for a big amount of data rows, then you may only once compile it into the tree of lambdas, and do not calculate any switches on each iteration at all;
For example, given such ast: {* (a, {+ (b, c)})}
Compile function (in very rough pseudo language) will be something like this:
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)}
}
}
So eventually compile returns the func, that must be called on each row of your data and there will be no switches or other calculation stuff at all.
There remains the question about operations with data of different types, that is for your own research
This is an interesting approach, in situations, when there is no jump-table mechanism available but sure, func call is more complex operation then jump.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论