编译器目前对大型结构体进行保守的编译。如果一个结构体类型有超过4个字段(或其他一些条件),我们将其视为不可SSA(Static Single Assignment,静态单赋值)的。对该类型变量的所有操作都指向栈,就好像它们的地址被占用了一样。
这种做法在很多方面都不理想。例如:
type T struct {
a, b, c, d int
}
func f(x *T) {
t := T{}
*x = t
}
type U struct {
a, b, c, d, e int
}
func g(x *U) {
u := U{}
*x = u
}
f
被优化地编译为:
XORPS X0, X0
MOVQ "".x+8(SP), AX
MOVUPS X0, (AX)
MOVUPS X0, 16(AX)
RET
g
差了很多:
MOVQ BP, 40(SP)
LEAQ 40(SP), BP
MOVQ $0, "".u(SP)
XORPS X0, X0
MOVUPS X0, "".u+8(SP)
MOVUPS X0, "".u+24(SP)
MOVQ "".u(SP), AX
MOVQ "".x+56(SP), CX
MOVQ AX, (CX)
LEAQ 8(CX), DI
LEAQ "".u+8(SP), SI
DUFFCOPY $868
MOVQ 40(SP), BP
ADDQ $48, SP
RET
我们在栈上将临时变量置零,然后将其复制到目标位置。
我们也应该通过SSA处理大型结构体。这将在SSA后端引入相当多的额外工作,如引入结构体构建器、任意宽度的选择器、大类型的栈分配、如果它们真的非常巨大,可能还需要堆分配等。
大小大于1的数组处于类似的状态,但它们有点更难处理,因为非常量索引会增加另一个复杂性。
9条答案
按热度按时间1cklez4t1#
请问一个显而易见的问题,我们想要处理所有n个问题,还是说n<=4太小了?如果是后者,并且合理的上限是16,那么可能更容易只是扩展当前的方法,也许可以使用一些代码生成的魔法。我们应该正确地做这件事,但值得问一下......
6ovsh4lw2#
我的目的是处理所有的n。
qyuhtwio3#
Looks like this should also help #20859
dw1jzc5e4#
https://golang.org/cl/106495提到了这个问题:
cmd/compile: add some generic composite type optimizations
xggvc2p65#
https://golang.org/cl/206937提到了这个问题:
cmd/compile: optimize big structs
8iwquhpp6#
对于Go工具链v1.16,具有8个int字段和[8]int值的结构体已经被视为小尺寸值。
dgjrabp27#
它现在确实编译成了更好的代码,但我认为根本问题仍然存在。我认为它只是需要一个更好的测试用例。
f
编译成MOVL $3, AX; RET
。g
编译成3npbholx8#
我对这个项目的状态很感兴趣,你能告诉我吗?
CL206937 项目被放弃了,但原因尚不清楚。
从那个实验中有什么额外的见解吗?
gfttwv5a9#
它并没有被遗弃,只是尚未完成。它存在一些问题。性能不佳,并且存在病理性案例,例如处理大型结构体的大型领域时表现不佳。