gpt4 book ai didi

rust - 装箱类型时是否有最佳实践?

转载 作者:行者123 更新时间:2023-11-29 07:48:14 26 4
gpt4 key购买 nike

在 C# 中,有结构和类。结构通常(即有异常(exception))分配在堆栈上,而类总是分配在堆上。因此,类实例对 GC 施加压力,被认为比结构“慢”。微软有 a best practice guide何时在类上使用结构。这表示在以下情况下考虑结构:

  • It logically represents a single value, similar to primitive types (int, double, etc.).
  • It has an instance size under 16 bytes.
  • It is immutable.
  • It will not have to be boxed frequently.

在 C# 中,使用大于 16 字节的结构实例通常被认为比垃圾收集类实例(动态分配)的性能更差。

就速度而言,盒装实例(堆分配)何时比非盒装等效实例(堆栈分配)表现更好?关于何时应该动态分配(在堆上)而不是坚持默认的堆栈分配,是否有任何最佳实践?

最佳答案

TL;DR:从无拳击开始,然后是侧写。


堆栈分配与盒装分配

这可能更明确:

  • 坚持堆栈,
  • 除非值(value)大到足以将其炸毁。

虽然在语义上编写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()
}
}

这里,没有魔法:

  • 如果编译器将返回槽用于one,那么如果分支评估为false,它必须将one移出然后实例化把新的WithDrop放进去,最后销毁一个
  • 如果编译器在当前堆栈上实例化one,并且必须返回它,那么它必须执行复制。

如果类型不需要Drop,就没有问题。

尽管存在这些奇怪的情况,但我建议尽可能坚持使用堆栈,除非分析显示有利于装箱的地方。


内联成员(member)或盒装成员(member)

这种情况要复杂得多:

  • struct/enum 的大小受到影响,因此 CPU 缓存行为受到影响:

    • 不太常用的大变体很适合装箱(或装箱的一部分),
    • 访问频率较低的大成员很适合进行装箱。
  • 同时还有装箱的成本:

    • 它与Copy 类型不兼容,并且隐式实现了Drop(如上所示,它禁用了一些优化),
    • 分配/释放内存具有无限延迟1,
    • 访问盒装内存会引入数据依赖性:在知道地址之前,您无法知道要请求哪个缓存行。

因此,这是一个非常好的平衡行为。对成员进行装箱或拆箱可能会提高代码库某些部分的性能,同时降低其他部分的性能。

绝对没有放之四海而皆准的办法。

因此,我再一次建议避免装箱,直到分析揭示一个对装箱有益的地方。

1 考虑在 Linux 上,进程中没有空闲内存的任何内存分配都可能需要系统调用,如果操作系统中没有空闲内存,系统调用可能会触发OOM killer 杀死一个进程,此时它的内存被回收并可用。一个简单的 malloc(1) 可能很容易需要 毫秒

关于rust - 装箱类型时是否有最佳实践?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45634972/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com