Noe*_*mer 6 heap stack boxing memcpy rust
在C#中,有结构和类.结构通常(即有异常)堆栈分配,类总是堆分配.因此,类实例会对GC施加压力,并且被认为比结构"慢".Microsoft有一个最佳实践指南何时在类上使用结构.这表示在以下情况下考虑结构:
- 它在逻辑上表示单个值,类似于原始类型(int,double等).
- 它的实例大小小于16个字节.
- 这是不可改变的.
- 它不必经常装箱.
在C#中,通常认为使用大于16字节的结构实例比垃圾收集类实例(动态分配)更糟糕.
在速度方面,盒装实例(堆分配)何时比非盒装等效实例(堆栈分配)更好?关于何时应该动态分配(在堆上)而不是坚持默认的堆栈分配,是否有任何最佳实践?
TL; DR:从没有拳击开始,然后是个人资料.
堆栈分配与盒装分配
这可能更明确:
虽然语义写入fn foo() -> Bar意味着Bar从被调用者帧移动到调用者帧,但实际上你更有可能最终得到一个fn foo(__result: mut * Bar)签名的等价物,其中调用者在其堆栈上分配空间并将指针传递给被调用者.
这可能并不总是足以避免复制,因为某些模式可能会阻止直接在返回槽中写入:
fn defeat_copy_elision() -> WithDrop {
let one = side_effectful();
if side_effectful_too() {
one
} else {
side_effects_hurt()
}
}
Run Code Online (Sandbox Code Playgroud)
在这里,没有魔力:
one,那么在分支求值的情况下false它必须one移出然后将new实例化WithDrop到其中,最后销毁one,one在当前堆栈上实例化,并且必须返回它,那么它必须执行复制.如果类型不需要Drop,就没有问题.
尽管有这些古怪的情况下,我建议坚持堆栈如果可能的话,除非分析显示它会是有益的箱子的地方.
内联会员或盒装会员
这种情况要复杂得多:
struct/ 的大小enum受到影响,因此CPU缓存行为受到影响:
与此同时,拳击有成本:
Copy类型不兼容,并且隐式实现Drop(如上所述,它禁用了一些优化),结果,这是一个非常好的平衡行为.对成员进行装箱或拆箱可以提高代码库某些部分的性能,同时降低其他部分的性能.
绝对没有任何一种尺寸适合所有人.
因此,我再一次建议避免拳击,直到剖析显示一个有利于盒子的地方.
1 考虑到在Linux上,进程中没有备用内存的任何内存分配都可能需要系统调用,如果操作系统中没有备用内存,可能会触发OOM杀手杀死一个进程,此时它的内存被打捞并提供.简单malloc(1)可能很容易需要几毫秒.