静态方法与实例方法的性能

我的问题与静态方法相对于实例方法的性能特征及其可伸缩性有关。对于此场景,假设所有类定义都在一个程序集中,并且需要多个离散指针类型。

考虑一下:

public sealed class InstanceClass
{
public int DoOperation1(string input)
{
// Some operation.
}


public int DoOperation2(string input)
{
// Some operation.
}


// … more instance methods.
}


public static class StaticClass
{
public static int DoOperation1(string input)
{
// Some operation.
}


public static int DoOperation2(string input)
{
// Some operation.
}


// … more static methods.
}

上面的类表示一个助手样式模式。

在实例类中,解析实例方法需要花费一些时间,与 StaticClass 相反。

我的问题是:

  1. 当保持状态不是一个问题(不需要字段或属性)时,使用静态类是否总是更好?

  2. 如果有相当数量的这些静态类定义(例如100个,每个都有许多静态方法) ,与相同数量的实例类定义相比,这会对执行性能或内存消耗产生负面影响吗?

  3. 当调用同一个实例类中的另一个方法时,是否仍然执行实例解析?例如,在同一个实例的 DoOperation1中使用像 this.DoOperation2("abc")这样的[ this ]关键字。

73796 次浏览

当保持状态不是一个问题时(没有字段或属性是 (必需) ,使用静态类是否总是更好?

我会说,是的。就像声明某个 static一样,你声明了一个无状态执行的 意图(它不是强制性的,而是人们期望的某种意图)

这些静态类的数量相当可观(比如说100个 例如,每个方法都有许多静态方法) 执行性能或内存消耗与之相比为负值 使用相同数量的实例类?

不要这样认为,除非您确定静态类是 真的无状态的,否则很容易搞乱内存分配并导致内存泄漏。

当[ this ]关键字用于调用同一 实例类,实例解析是否仍然发生?

不确定,大约是 这个点(这是 CLR 的一个纯实现细节) ,但是认为是的。

静态方法更快,但 OOP 更少。如果使用的是设计模式,静态方法可能是不好的代码。业务逻辑最好写成非静态的。像文件读取、 WebRequest 等常用函数最好是静态的。你的问题没有普遍的答案。

理论上,静态方法的性能应该比实例方法稍好一些,其他条件都相同,因为有额外的隐藏 this参数。

在实践中,这几乎没有什么区别,以至于它会隐藏在各种编译器决策的噪音中。(因此,两个人可以用不同的结果“证明”一个人比另一个人更好)。尤其是因为 this通常是在一个寄存器中传递的,并且通常是从该寄存器开始的。

最后一点意味着,在理论上,我们应该期望一个静态方法将一个对象作为一个参数,并使用它做一些事情,这种方法的效果比同一个对象上的等价实例稍差一些。尽管如此,这种差异是如此微小,以至于如果您尝试测量它,您可能最终会测量其他编译器的决策。(特别是如果这个参考一直在一个寄存器中的可能性也很高)。

真正的性能差异将取决于您是否人为地让内存中的对象执行本应是静态的操作,或者您是否以复杂的方式将对象传递链缠绕在一起,以执行本应是实例的操作。

所以是1号。当保持状态不是一个问题时,最好是静态的,因为这就是静电干扰的作用。这不是一个性能问题,尽管有一个与编译器优化良好相处的总体规则——更有可能的是,有人努力优化那些正常使用的情况,而不是那些奇怪使用的情况。

二号。没什么区别。对于每个成员来说,每个类都有一定的成本,包括有多少元数据,实际的 DLL 或 EXE 文件中有多少代码,以及有多少抖动的代码。不管是实例还是静态,这都是一样的。

在第3项中,thisthis一样,但是要注意:

  1. this参数在特定的寄存器中传递。当在同一个类中调用一个实例方法时,它很可能已经在那个寄存器中了(除非它被隐藏起来并且由于某种原因使用了这个寄存器) ,因此不需要任何操作来将 this设置为它需要设置的值。这在一定程度上适用于例如方法的前两个参数是它调用的前两个参数。

  2. 因为很明显 this不是 null,所以在某些情况下可以用它来优化调用。

  3. 因为很明显 this不是 null,这可能会再次提高内联方法调用的效率,因为为假冒方法调用而生成的代码可能会忽略它可能需要的一些 null 检查。

  4. 也就是说,无效检查很便宜!

值得注意的是,如果给定的静态方法没有针对给定的类型调用,那么作用于对象的通用静态方法(而不是实例方法)可以减少 http://joeduffyblog.com/2011/10/23/on-generics-and-some-of-the-associated-overheads/中讨论的一些开销。正如他所说,“顺便说一句,事实证明,扩展方法是一种很好的方法,可以使通用抽象更加物有所值。”

但是,请注意,这仅与该方法使用的其他类型的实例化有关,而这些类型在其他情况下是不存在的。因此,它实际上并不适用于很多情况(其他一些实例方法使用该类型,其他一些代码使用该类型)。

摘要:

  1. 大多数情况下,实例与静态的性能成本低于可忽略不计。
  2. 有什么成本,通常会来你滥用静态例如或反之亦然。如果不将其作为静态和实例之间决策的一部分,则更有可能得到正确的结果。
  3. 在很少的情况下,其他类型的静态泛型方法会比实例泛型方法创建更少的类型,这使得 有时候有一个很小的好处,就是很少使用(“很少”指的是它在应用程序的生命周期中与哪些类型一起使用,而不是它被调用的频率)。一旦你理解了他在那篇文章中所谈到的内容,你就会发现,无论如何,这与大多数静态 VS 实例的决策都是100% 无关的。编辑: 而且大多数情况下,只有 ngen 有这个成本,而不是 jitted 代码。

编辑: 一个关于空检查是多么便宜的说明(我在上面声明过)。大多数空检入。NET 根本不检查 null,而是继续假设它会正常工作,如果发生访问异常,它就会变成 NullReferenceException。因此,大多数情况下,当 C # 代码由于访问实例成员而在概念上涉及空检查时,成功的代价实际上为零。一个例外是一些内联调用(因为它们想表现得像调用了一个实例成员一样) ,它们只是触发一个字段来触发相同的行为,所以它们也非常便宜,而且它们仍然经常被忽略(例如,如果方法的第一步涉及到访问一个字段)。