func (c *conn) setState(nc net.Conn, state ConnState) {
...
c.curState.Store(connStateInterface[state])
...
}
// connStateInterface is an array of the interface{} versions of
// ConnState values, so we can use them in atomic.Values later without
// paying the cost of shoving their integers in an interface{}.
var connStateInterface = [...]interface{}{
StateNew: StateNew,
StateActive: StateActive,
StateIdle: StateIdle,
StateHijacked: StateHijacked,
StateClosed: StateClosed,
}
我无法用connStateInterface弄清楚这个窍门,它到底是如何工作的?
这里发生了几件事...
该[...]
声明创建一个实际的数组而不是一个切片,从而删除了间接寻址。这里声明的是一个数组interface{}
类型,所以您可能想知道为什么奇怪的地图符号?
该StateXXX
变量只是常量宣布进一步之上,所以他们是整数...这样的声明实际上是形式为index: value
。
这是一个使用整数数组的不太混淆的示例:
var i = [...]int{4: 2, 2: 7}
这将分配一个包含以下内容的数组:
[0, 0, 7, 0, 2]
...请注意,索引2的值为7,索引4的值为2。这不是声明数组的常用方法,但它是有效的Go语言。
因此,回到原始声明,仅以我上面给出的示例为例,而不是int,而是创建type数组interface{}
:
var i = [...]interface{}{4: 2, 2: 7}
您将得到一个类似的数组,但是nil
接口值代替零。
更加接近原始代码,StateXXX
常量只是int,而不是像我的示例中那样的文字。
那么,这一切的意义何在?为什么所有的混淆?
这是一个性能黑客。该函数c.curState.Store()
接受类型为的参数interface{}
。如果将其传递给int,则编译后的代码将不得不在每次调用时转换类型。关于此的更清晰(尽管显然不切实际)的说明可能是:
var val interface{}
for i := 0; i < 1000000; i++ {
// the types are different, compiler has to fumble int vs. interface{}
val = i
// do something with val
}
每次您val = i
在之间进行转换int
并interface{}
需要发生时。您发布的代码通过创建一个静态查找表(其中所有值都已经是interface类型)来避免这种情况。
因此,这:
c.curState.Store(connStateInterface[state])
比这更有效:
c.curState.Store(state)
由于state
在这种情况下将需要进行int -> interface{}
转换。在优化的代码中,state
仅仅是一个索引,它将一个值查找到一个数组中,其结果使您获得一个interface{}
...,因此int -> interface{}
避免了类型转换。
我不熟悉该代码,但我想它处于关键路径,纳秒级或任何节省下来的成本都可能会有所作为。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句