任何人都可以理解connStateInterface吗?

时间:2018-07-13 06:45:42

标签: go

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弄清楚这个窍门,它是如何工作的?

1 个答案:

答案 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 = iint之间的转换时,都需要发生。您发布的代码通过创建一个静态查找表来避免这种情况,该表中的所有值均已类型。

因此,这:

interface{}

比这更有效:

c.curState.Store(connStateInterface[state])

在这种情况下,由于c.curState.Store(state) 需要进行state转换。在优化的代码中,int -> interface{}只是一个将值查找到数组中的索引,其结果为您带来state ...,因此避免了interface{}类型转换。 / p>

我不熟悉该代码,但我想它处于关键路径,纳秒级或任何节省下来的成本都可能会有所作为。