【发布时间】:2011-01-09 16:02:04
【问题描述】:
在内存和时间方面,将方法设为静态会更好吗?
【问题讨论】:
-
回答你的问题:是的,但你永远不应该考虑到这一点。请参阅 LBushkin 的回答。
-
完全改写了问题。听起来您指的是使方法静态化的实际过程。 ;) 重构工具使这个过程更快!
标签: c# performance static-methods
在内存和时间方面,将方法设为静态会更好吗?
【问题讨论】:
标签: c# performance static-methods
这类问题有很多。静态方法更快/更慢吗?虚拟功能更快/更慢吗? i++ 是否比 ++i 快/慢? for(;;) 比 while(true) 快/慢吗?
值得拥有一些软件和tuning it。这将使您对实际影响软件性能的事物有一个很好的认识。
然后您会发现,此类问题的答案(大多数情况下)是微不足道的。
如果我可以概括的话,根据我的经验,使软件变慢的原因是使用了看似无害的代码行,但其时间消耗可能比想象的要大几个数量级。由于它们看起来很无辜,因此无法通过查看代码可靠地找到它们。示例:重复分配、初始化和释放大型数据结构只是为了确保它们存在。国际化不需要的字符串。通知风格的编程,可以将一个简单的属性设置转变为整个大型数据结构中的大量方法调用。简单的 O(n^2) 操作,除非 n 变大,否则永远不会成为问题。认为 (a
【讨论】:
静态方法和非虚拟实例方法之间几乎没有区别。后者只是将this prointer/reference 作为“隐藏”参数。在生成的机器代码中,两种调用看起来非常相似。
如果方法不依赖/修改对象,则它应该是静态的。
另一方面,虚拟方法(可覆盖)要求调用者在所谓的vtable 中查找确切的实现。除了防止编译器内联非常小的方法(简单的属性访问器经常内联)之外,查找还需要几个周期。
不过,虚方法是 C#/CLR 上最快的一种动态调度。比委托和反射快得多。
【讨论】:
我发现我在给定类中编写的许多实用程序方法只需要类中的几个数据成员,或者根本不需要。在这些情况下,我倾向于将这些方法编写为(独立的)静态方法,直接传入它们需要的少数数据项。
如果它们对其他类特别通用且足够有用,我也可以将它们公开。
【讨论】:
通常是的,不需要传递“this”引用。该引用在 ECX 寄存器中传递,因此不需要额外的堆栈空间。如果您从同一类的实例方法进行调用,则该寄存器已经设置,根本不会节省任何费用。但是当方法在另一个类中时,它可以帮助减轻x86 CPU内核的压力,x86没有很多寄存器。看到可衡量的性能改进是极其罕见的。
我确实将不使用实例成员的类的方法标记为静态的。我看重静态关键字提供的内在契约:“此方法不会改变对象状态。”
【讨论】:
static变得无价。
如果方法不需要来自其所属类的任何状态信息,则应将其设为静态。
如果您不关心多态性,您可以将任何方法编写为实例或静态,只需决定是否采用类实例成员并将它们作为参数传递给方法。 你应该考虑的,是语法是否自然,代码是否易于理解和有意义等等。
您可能不必担心在此级别进行优化,因为实例与静态方法的性能开销可以忽略不计。是的,调度表中使用了一些空间用于类型(如果方法是虚拟的) - 但这是一个很小的、恒定的开销。是的,与静态方法相比,调用实例方法的开销也很小 - 但同样很小。
这似乎是某种程度的微优化,除非您有可衡量的、有形的证据相信确实会影响程序性能,否则您应该避免这样做。事实上,如果你做错了,传递额外参数(将它们复制到堆栈等)而不是通过你的类型的隐藏 this 引用访问它们的成本可能会导致更糟 性能。
你最好分析方法的语义,并在此基础上做出静态/实例决策。
【讨论】:
callvirt。我不确定它实际上在多大程度上做到了这一点,但这是可能的。
callvirt 必须用于跨程序集边界的调用,因为将方法设为虚拟是一项非破坏性更改(而将其设为虚拟->非虚拟将是)。由于虚拟方法在 CLI 中是显式的(而不是在 JVM 中是隐式的),即使在非优化模式下,JIT 也可以确定是使用直接调度还是间接调度。唯一的开销是空值检查,这在当前处理器上几乎是免费的。
在大多数情况下差异可以忽略不计,但静态更有效。
在静态方法调用中避免了以下步骤:
【讨论】:
如果你还是要传递实例 (SomeStaticMethod(obj, "abc", 123);),那不是真的。您只能在没有多态性的场景中有效地使用静态方法,在这种情况下,任何简单的东西很可能无论如何都会被内联。
“自然地”使用对象 (obj.SomeMethod("abc",123);) - 保持代码简单,profile 发现性能问题 - 非常不太可能在实例和静态,除非您正在运行一些 非常 紧密的循环。 有一些场景可能很重要,但它们非常专业。
【讨论】: