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弄清楚这个窍门,它是如何工作的?
答案 0 :(得分:7)
这里发生了几件事...
[...]
声明创建一个实际的数组而不是一个切片,从而删除了间接寻址。这里声明的是一组interface{}
类型的数组...所以您可能想知道为什么奇怪的地图外观符号?
StateXXX
变量只是上面进一步声明的常量,因此它们是整数...因此声明实际上是index: value
形式。
下面是使用整数数组进行混淆的示例:
var i = [...]int{4: 2, 2: 7}
这将分配一个包含以下内容的数组:
[0, 0, 7, 0, 2]
...请注意,索引2的值为7,索引4的值为2。这不是声明数组的常用方法,但是它是有效的Go。
因此,回到原始声明,仅以我上面给出的示例为例,而不是int,使数组类型为interface{}
:
var i = [...]interface{}{4: 2, 2: 7}
您将获得一个类似的数组,但是使用nil
接口值代替零。
更加接近原始代码,StateXXX
常量只是一个整数,而不是像我的示例中那样的文字。
那么,这一切的意义何在?为什么所有的混淆?
这是一个性能黑客。函数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{}
比这更有效:
c.curState.Store(connStateInterface[state])
在这种情况下,由于c.curState.Store(state)
需要进行state
转换。在优化的代码中,int -> interface{}
只是一个将值查找到数组中的索引,其结果为您带来state
...,因此避免了interface{}
类型转换。 / p>
我不熟悉该代码,但我想它处于关键路径,纳秒级或任何节省下来的成本都可能会有所作为。