使用 go1.12
假设我们有一个未导出的全局变量:
var mySlice = make([]T, 4)
并且对这个切片执行的操作中没有以下情况:
- 让对切片本身的引用从包中逃逸(意味着该变量是未导出的)
- 修改切片头本身(例如,不给变量赋值)
例如,允许的操作有元素赋值(例如,mySlice[3] = ...
)和元素寻址(例如,&mySlice[3]
)。
如果满足上述条件,我认为在初始大小以下的切片中的索引可以省略边界检查,因为索引是确信不会超过边界的。
这种情况的使用场景如下:
var globalMessageTypes = make([]protoimpl.MesageType, 2)
func (T) Type() protoreflect.MessageType {
return &globalMessageTypes[0] // Desire no bounds check here
}
func (R) Type() protoreflect.MessageType {
return &globalMessageTypes[1] // Desire no bounds check here
}
func init() {
for i := range ... {
globalMessageTypes[i] = ...
}
}
我们可以使用数组来避免边界检查,但这也有其他问题(参见#30528)。
5条答案
按热度按时间vlju58qv1#
检查一个变量是否被赋值过是耗费资源的,有时几乎是不可能的。但是我们可以对this proposal中描述的最终结果这样做。#29422的另一个好处。
yquaqz182#
假设你声称这是"代价高昂,有时几乎不可能",这并不有利于你的提案,该提案依赖于编译器能够进行这种类型的分析(以强制执行建议的新语言语义)。
我应该指出,在编译器中解决这个问题并不需要像#29422这样完整的提案所需的严格正确性证明。如果编译器不确定变量是否被变异,它总是可以采取安全的做法,即发出边界检查。
628mspwn3#
我认为在某些情况下,代码的作者可以确保全局值永远不会被修改,但编译器很难得出同样的结论。
举一个简单的例子,如果全局切片作为参数传递给许多函数,而这些函数继续将相应的参数传递给更多的其他函数。对于编译器来说,推断切片是否会被修改是非常耗费成本的。
b5buobof4#
我的意思是,让编译器推断出某些全局切片永远不会被修改(如果推断工作不昂贵)并不是一件坏事。然而,让代码作者标记一些永远不会被修改的值会更好。
mwkjh3gx5#
优化传递,包括移除边界检查,是在每个函数上独立完成的。你是否尝试过将你问题的所有方面都放在同一个函数中的例子?