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