go ``` cmd/compile: 使用位测试在类型开关中进行二分查找 ```

dldeef67  于 6个月前  发布在  Go
关注(0)|答案(3)|浏览(56)

考虑一个具有常量(非接口)情况的大型类型开关,例如:

package p

func f(e interface{}) int {
	switch e.(type) {
	case int:
		return 0
	case bool:
		return 1
	case byte:
		return 2
	case uint16:
		return 3
	case string:
		return 4
	}
	return 5
}

生成的代码使用 < 对类型情况进行哈希的二分查找。然而,实际值(即哈希值)很大,因此生成的代码包含用于进行比较的大常量。对于上述代码,使用 1.11,以下是中间部分的摘录:

0x000a 00010 (type_switch_opt.go:7)	MOVL	16(AX), CX
	0x000d 00013 (type_switch_opt.go:7)	CMPL	CX, $1715356255
	0x0013 00019 (type_switch_opt.go:7)	JHI	99
	0x0015 00021 (type_switch_opt.go:7)	CMPL	CX, $335480517
	0x001b 00027 (type_switch_opt.go:7)	JNE	91

但是由于这些常量是已知的哈希值,我们可以使用位测试来进行二分查找:首先根据 hash&1 != 0 进行分割,然后是第二个位,依此类推,根据需要进行分割。这应该提供较短的指令编码,甚至可能更快的执行速度。(这可能只适用于某些架构;需要进一步调查。)最好在编译时确认位测试是否能很好地将输入(几乎)分成两半,并在候选位不起作用时继续处理更高位。

bvhaajcl

bvhaajcl1#

编译器可以计算由位测试产生的每个可能的分割中的熵(也许在某些架构上,我们可能会使用多个标志来进行多于2种方式的分割,例如移位操作),并选择导致信息增益最大的分割。重复此过程,直到只剩下switch分支的案例项。这样的决策树构建机制甚至可以通过后期的性能指导优化数据进行增强,并用于非类型开关。

0lvr5msh

0lvr5msh2#

@martisch,对于所有非类型开关来说,这并不总是起作用得很好,因为非类型开关通常具有更自然的结构,例如连续的值范围,这些值都Map到相同的情况,而在树的较高层次上仍然做出乐观决策时,找出如何利用这一点就有点困难了。类型开关使用哈希使得它们更容易和简单,因此是一个很好的起点。

42fyovps

42fyovps3#

@josharian 同意哈希应该有一个更好的分布,可以更好地利用。绝对是一个很好的起点。我在想这可以被测试和扩展到例如大型枚举类型,如开关,一旦有基础设施,编译器可以使用它来决定将此应用于更广泛的适用开关范围。然而,枚举通常较小,因此比较的大小差异似乎不那么重要。可能会有一些简单的过滤启发式方法,甚至不会尝试在具有范围的开关上应用这种方法,因此在编译过程中不会浪费工作。但所有这些都是未来的主题。最好从简单的开始,首先开发、测试和基准测试与类型开关配合良好的东西,这是这个问题的范围。

相关问题