【问题标题】:Why should a .NET struct be less than 16 bytes?为什么 .NET 结构应小于 16 个字节?
【发布时间】:2010-11-08 02:43:14
【问题描述】:

我已经在几个地方读到,结构的最大实例大小应该是 16 字节。

但我看不出那个数字 (16) 是从哪里来的。

在网上浏览时,我发现一些人认为这是表示良好性能的近似数字,但微软认为这是一个硬性上限。 (例如MSDN

有人对为什么它是 16 字节有明确的答案吗?

【问题讨论】:

    标签: c# .net


    【解决方案1】:

    如果一个结构不大于 16 字节,则可以使用一些简单的处理器指令来复制它。如果较大,则使用循环来复制结构。

    只要结构不大于 16 字节,处理器在复制结构时必须执行与复制引用时相同的工作。如果结构较大,您将失去拥有结构的性能优势,通常应该将其改为类。

    【讨论】:

    • 我不是 x86 汇编大师,哎呀,我可能实际上是一个完整的 n00b - 但我真的很好奇,你能用一些示例代码更新你的答案来证明这一点吗?处理器运行在 32 位还是 64 位模式下是否重要?
    • @Goyuix:32位和64位代码当然会有一些性能差异,但遵循相同的原则。不久前我做了一个性能测试。代码只是很多结构和很多循环,所以这不是很有趣,但你可以在这里看到结果:stackoverflow.com/questions/2437925/…
    • 在我看来这是一个正确的答案,但我不确定为什么它被大部分人忽略了。您其他答案的性能比较表非常有说服力。
    • 小提示:“廉价复制”的阈值曾经是 16 字节,但现在已经增长。最后我检查了一下,x86 是 20 字节,x64 是 24 字节。
    • 要以有效的方式复制 16 个字节,是否使用 XMM(128 位)寄存器? AFAIK x64 处理器上的大多数寄存器都是 64 位的。
    【解决方案2】:

    正如其他答案所指出的,复制大于某​​个阈值(在早期版本的 .NET 中为 16 个字节,但后来增长到 20-24 个字节)的结构的每字节成本明显高于较小结构的每字节成本。然而,重要的是要注意,复制 any 特定大小的结构 once 将只是创建相同大小的新类对象实例成本的一小部分。如果一个结构在其生命周期中会被复制很多次,并且值类型语义不是特别需要,那么类对象可能更可取。然而,如果一个结构最终只被复制一次或两次,那么这种复制可能比创建一个新的类对象更便宜。类对象变得更便宜的盈亏平衡数量随所讨论的结构/对象的大小而变化,但对于低于“廉价复制”阈值的东西,比上面的东西要高得多。

    顺便说一句,另一点值得一提的是,将结构作为ref 参数传递的成本与结构的大小无关。在许多情况下,可以通过使用值类型并通过ref 传递它们来实现最佳性能。但是,必须小心避免使用结构类型的属性或 readonly 字段,因为访问其中任何一个都会创建相关结构的隐式临时副本。

    【讨论】:

    • 我不知道这部分关于使用属性或readonly 字段。你能给我指向一个链接以供进一步阅读吗?
    • @Justin:属性获取器只不过是一个方法,其返回类型是相关属性的类型。因此,该属性必须将信息复制到用于返回值的任何寄存器或内存位置。访问任何属性或方法时会复制只读字段,因为编译器无法知道被调用的方法是否会尝试更改调用它的结构。与 C# 不同,.net 没有阻止结构方法更改传递给它的结构的机制。微软可以通过两种方式处理这种情况:
    • (1) 允许将只读结构直接传递给方法,并希望没有人试图将它们传递给变异方法,或者 (2) 制作只读结构的临时副本,并将该副本传递给被调用的方法。请注意,(2)将比(1)慢,并且在(1)也不会产生正确行为的情况下通常不会产生正确的行为; (2) 所做的是在尝试更改只读结构的方法时更改错误行为的性质。
    • “幕后 .net 内存管理”——作者:Chris Farrell、Nick Harrison:您可能想知道 16 字节的限制,特别是因为没有强制执行此限制。推理来自创建对象的开销。在 32 位机器上,12 个字节仅用于开销——一个 8 字节的标头和一个 4 字节的引用。因此,当一个对象没有至少 16 字节的数据时,您的设计可能非常低效。考虑将此类对象转换为开销不大的结构。
    • @UladzimirSharyi:.NET 从使用更快的代码复制结构切换到使用更小但更慢的代码存在一个大小阈值。我认为这曾经是 16 个字节,但它已经增长了一点。对于许多使用模式,实际的收支平衡点在 16 字节以上——有时远高于 16 字节。
    【解决方案3】:

    这是结构可以表现出卓越性能的场景:

    当您需要创建 1000 个实例时。在这种情况下,如果您要使用一个类,您首先需要分配数组来保存 1000 个实例,然后在循环中分配每个实例。但是,如果您要使用结构,那么在您分配要保存它们的数组后,这 1000 个实例将立即可用。

    此外,当您需要进行互操作或出于性能原因想要深入了解不安全的代码时,结构非常有用。

    与往常一样,需要权衡取舍,需要分析他们正在做的事情,以确定实施某事的最佳方式。

    ps:当我使用 LIDAR 数据时,这种情况开始发挥作用,其中可能有数百万个点表示 x、y、z 和地面数据的其他属性。需要将这些数据加载到内存中进行一些密集计算以输出各种内容。

    【讨论】:

      【解决方案4】:

      这只是一个经验法则。

      关键是因为值类型是按值传递的,如果将结构体传递给函数,则必须复制整个结构的大小,而对于引用类型,只需复制引用(4字节) .虽然结构可能会节省一些时间,因为您删除了一层间接,因此即使它大于这 4 个字节,它仍然可能比传递引用更有效。但在某些时候,它变得如此之大,以至于复制成本变得显而易见。一个常见的经验法则是,这通常发生在 16 个字节左右。选择 16 是因为它是一个很好的整数,是 2 的幂,备选方案是 8(太小,会使结构几乎无用)或 32(此时复制结构的成本已经成问题如果您出于性能原因使用结构)

      但归根结底,这是性能建议。它回答了“使用哪个最有效?结构还是类?”的问题。但它没有回答“哪个最好映射到我的问题域”的问题。

      结构和类的行为不同。如果你需要一个结构的行为,那么我会说让它成为一个结构,不管大小。至少在您遇到性能问题之前,分析您的代码并发现您的结构存在问题。

      您的链接甚至说这只是性能问题:

      如果这些条件中的一个或多个 不满足,创建引用类型 而不是结构。未能 遵守本指南即可 对性能产生负面影响。

      【讨论】:

      • 是的,该链接确实说这是一个性能问题,但它使用的语言也非常强大,即“不要定义结构......”。他们本可以说“这是不可取的……”
      • 没错,措辞似乎有点强。但这可能是为了强调堆分配的类并不慢(正如来自 C/C++ 的程序员所期望的那样)
      • 对于精确数字的一个可能答案是 16 字节结构仍然足够小以适应 CPU 的内存总线,或者可以作为 SIMD 指令的一部分进行复制。较大的结构在复制或读取/写入时变得更加复杂。
      • @Backwards_Dave:在 32 位平台上,复制 4 个字节的倍数不会比复制 1、2 或 3 个字节少。同样对于 64 位平台和 8 的倍数(并且复制少 1-7 个字节)。由于后一点,在推荐中使用八的倍数是有意义的。我认为 24​​ 会比 16 更好,但结构或类是否更有效取决于使用模式。在某些使用模式中,保存 16 字节数据的类会比结构体执行得更好,而在某些情况下,结构体...
      • ...保存 64 字节的数据会比一个类执行得更好。我认为 24​​ 字节或更少字节的结构的性能显着不如一个类的情况要多于类性能显着不太好的情况。
      【解决方案5】:

      大小数字主要来自复制堆栈上的结构所需的时间,例如传递给方法。任何比这大得多的东西,并且仅复制数据就会消耗大量堆栈空间和 CPU 周期 - 当对不可变类的引用(即使取消引用)可能会更有效。

      【讨论】:

      • 使用最新的 C#7.2 功能我认为您可以摆脱大部分数据复制浪费(如果使用得当)?
      【解决方案6】:

      我认为从性能的角度来看,16 个字节只是一个经验法则。 .NET 中的对象至少使用 24 字节的内存 (IIRC),所以如果你的结构比这大得多,那么引用类型会更好。

      我想不出他们为什么专门选择 16 字节。

      【讨论】:

      • .NET 中的对象使用至少 24 字节的内存 (IIRC)。你有这方面的参考吗?
      • @nawfal:在 .NET 中,每个对象在堆上都有两个机器字开销(32/64 位模式为 2x4 或 2x8 字节)。此外,对于任何用途的任何对象,都必须至少存在一个对其的引用,这将是另一个机器字。
      猜你喜欢
      • 2011-01-27
      • 2019-02-03
      • 1970-01-01
      • 2021-05-19
      • 1970-01-01
      • 1970-01-01
      • 2013-03-26
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多