【问题标题】:c# struct vs Class performace, Design focusc# struct vs Class 性能,设计重点
【发布时间】:2015-12-18 10:22:55
【问题描述】:

使用 struct over class 可以获得多少性能提升?我是否应该在设计应用程序时将重点放在最大限度地使用结构而不是类?

我是否可以将我的类转换为结构并将函数移动到另一个静态类中?

【问题讨论】:

  • 使用结构将避免额外的引用解除引用以访问结构的字段,但会增加成本,即每当您需要传递结构时,它都会被复制,除非您通过引用显式传递它.这可能很有用,但在类上全面使用结构很可能对您的代码有害。
  • 可能更重要的是避免动态分配。结构,即值类型,其行为与整数很相似;当您创建它们时,它们是本地或静态内存,比动态分配便宜得多。正如 Martin 所说,缺点是复制该值以进行赋值、返回等。这对于大型对象来说比返回引用更昂贵。

标签: c# class struct


【解决方案1】:

msdn 建议(在另一个答案中列出)提供了一些指导。为了性能,您应该考虑它们的用法以及结构和类之间的区别在哪里很重要。最重要的是只在类型是实际值类型时才使用结构,即具有值语义。将类用于值类型,或将结构用于应该是引用类型的东西,会很快产生令人困惑的结果,导致意外复制或意外引用。还要记住,结构应该始终是不可变的。

只有在对性能极为敏感的情况下,您才应该忽略任何这些基本规则(例如:框架破坏了 List.Enumerator 结构的规则!)。

结构与类的性能注意事项:

如果您将结构体作为参数传递给方法,则每次都会创建该结构体的副本。这就是文档建议不要使结构大于 16 字节的原因。

double Distance(Vector3D a, Vector3D b)   // Copies both 24-byte structs
{
   return Math.Sqrt((a.X - b.X *....);
}

在上面的场景中,3 个双精度的 3D 向量将产生 24 个字节,并且会比推荐的 16 个更大,但我仍然认为结构是有意义的,因为它显然是一个值类型,尤其是你有一个Vector2D 包含两个双精度(16 字节),是一个结构!

高效使用结构以及它们的性能真正闪耀的关键是将它们用于缓存位置并避免多次分配。重用上面的 Vector 例子,如果你有这个方法

double AverageDistanceFromOrigin(Vector3D[] points) // Single reference passed
{
   double sum = 0.0;
   for(...)                                         
      sum += Math.Sqrt(points[i].X... + ... + ...)  // No copies
   return sum/points.Length;
}

您可能会看到有利于结构的良好性能差异。原因是现在您将单个引用(数组)传递给该方法,因此每次调用复制结构没有额外的开销(请注意,该方法不会为数组中的每个条目调用距离方法) .

结构数组也像 [x1, y1, z1, x2, y2, ...] 这样在内存中连续布局,因此 cpu 将加载例如一次16个坐标进入缓存,导致缓存未命中率极低。将其与class Vector3D 实现进行比较:现在将分配一个向量数组,并且数组中的每个条目是一个必须在堆上分配的引用,然后是垃圾回收。该数组现在是一个引用数组 [ref to v1, ref to v2, ref to v3] 每一个都可能有堆中的任何地址,并且不能彼此相邻。这可能导致比结构案例更多的缓存未命中。

  • 仅当您的类型具有值语义时才使用结构(反之亦然)。
  • 不要将大型结构单独传递给方法;对结构列表/数组进行操作以避免复制
  • 不要将 16 字节限制视为硬限制:您可以使用更大的结构,但请记住避免将它们单独传递给方法。

【讨论】:

    【解决方案2】:

    MSDN 对此有好处:

    ✓ 考虑定义一个结构而不是一个类,如果 类型很小,通常寿命很短,或者通常嵌入 其他物体。

    X 避免定义结构,除非该类型具有所有 具有以下特点:

    1. 它在逻辑上表示单个值,类似于原始类型 (int、double 等)。
    2. 实例大小小于 16 字节。
    3. 它是不可变的。
    4. 不必经常装箱。

    MSDN 也有Struct Design

    我应该在设计应用程序时将重点放在最大限度地使用结构而不是类吗?

    这取决于您要实现的目标。当您尝试实现一个行为类似于值的小型结构时,将使用结构。所以说使用 struct over class 并不是一个理想的陈述或方法。

    【讨论】:

      【解决方案3】:

      简短的回答是“你可以,但你不应该”。

      类和结构的主要区别在于结构定义了值类型,而类定义了引用类型。因此,您实际上不能只用“struct”替换“class”一词并移动函数,因为您的程序的行为可能会受到上述差异的严重影响。然后,如果您正在从头开始设计一个新应用程序,是的,您可以考虑是否在适当的地方使用结构或类来定义您的逻辑对象,但是您的代码必须牢记这一点。 我希望这会有所帮助!

      【讨论】:

      • 我正在将大量行类型数据加载到 List 中,因为这就是 EF 实现数据的方式。然后我在“只读”庄园中处理这些数据,记住有一百万个,然后我将其分组为 10 000 个并发送到不同的线程,更改类列表是否更聪明结构列表?目前尚不清楚更改为结构是否有助于提高性能。
      • 听起来您正在批量处理大量记录。在这种情况下(您有大量数据并且性能至关重要),我个人会采用更“数据库特定”的方法(尽可能存储过程和 CLR 对象)而不是使用 ORM。也许使用 IEnumerables 而不是列表可以避免将所有内容加载到内存中进行处理,但是在数据库和您的业务逻辑之间仍然存在数百万次往返,您可以避免将逻辑移动到数据库。只是我的意见!
      • 感谢您的回复,不,我的表现像老板一样下降...我只是想了解是否真的可以通过增加很多复杂性来获得更多!通过更改为结构。一直在阅读负载,但从来没有真正找到一个很好的例子来说明我在想什么……甚至会产生差异,或者是一个好主意,因为 struct 的用例从未在我的消费方式中解释过。 aka 找不到任何使用 struct 处理数百万行/列类型数据的示例。从 EF 加载数据,然后我将数据作为类......处理行......
      【解决方案4】:

      使用 struct over class 可以获得多少性能提升?

      对于“可能”,答案是你可以快很多倍。

      但是它也可能慢很多倍。

      大多数情况下两者都不是,而只是稍微获得或损失。

      因此,“我将使用结构更快”本身就是一种错误的想法。

      如果您的出发点是考虑某个东西是否应该是ValueType,因为它确实应该是一个值类型;仅根据其价值来定义、使用和推理,或者不应该是ValueType,因为它不是这样的价值类型,那么对于该特定情况,您可能会有最高效的选择。 95% 或更多的时间。

      在剩下的 5% 中,如果您选择了另一种方式,那么其中可能有 90% 的性能只会低到可以忽略不计,并且/或者它们不会处于足够热的路径上,因此不会有影响。 (假设您在给定方法上节省了 20 毫秒。如果该方法被调用一百万次,那么您已经节省了五个半小时的运行时间,但如果在您节省 20 毫秒后调用它,您甚至不会注意到)。

      在出于性能原因我们实际上希望在值类型和引用类型之间进行更改的情况下,我们可能经常从struct 转到class,而不是相反。确实如此,避免值类型的大小超过 16 字节是一个经常被引用的准则,尽管这实际上是一种性能优化,而不是作为值或引用类型意味着什么的固有部分。

      还值得注意的是通过调用代码来区分值和引用类型。有些操作在可变结构的数组上比在可变引用类型的数组(或任何其他集合)上要快得多。但是利用这些需要你既使字段公开又使值类型可变;这两件事都会造成相当大的困难。这样的优化对于使用它们的类中的private 类型是合理的,并且对于internal 类型的推动是合理的,但是如果使用public 类型完成它就会自找麻烦。

      【讨论】:

        【解决方案5】:

        将结构转换为类不会自动提高应用程序的性能。
        这取决于你如何使用它们,以及你如何使用你的类对象。

        如果您创建和销毁一个类的大量对象,您可以重用该类的对象,或者创建一个结构并重用它
        (所以你看,你也可以用一个类来做到这一点)。

        当您调用方法并发送参数或返回值时,
        一个类按引用传递,一个结构按值传递
        所以在这里你可能会享受性能提升,如果你花很多时间。
        (再一次,通过良好的编程,您甚至可以通过将数据作为包含方法的类的字段来避免一直将其传递给方法)。

        关于你的第二个问题,
        您也可以在结构中拥有方法,您不一定需要将它们移动到静态类。

        struct SimpleStruct
        {
            private int xval;
        
            public int X
            {
                get 
                {
                    return xval;
                }
                set 
                {
                    if (value < 100)    xval = value;
                }
            }
        
            public void DisplayX()
            {
                Console.WriteLine("The stored value is: "+xval);
            }
        }
        

        https://msdn.microsoft.com/en-us/library/aa288471.aspx

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2014-05-26
          • 1970-01-01
          • 2010-12-13
          • 2016-09-09
          • 2013-04-30
          • 1970-01-01
          • 2012-12-04
          • 1970-01-01
          相关资源
          最近更新 更多