“现代面向对象语言几乎消除了进程内调用的开销”?

发布于 2024-08-05 08:48:09 字数 432 浏览 7 评论 0 原文

我正在阅读 Source Making 网站,特别是 重构 部分。在描述长方法问题的页面上,做了以下声明:

较旧的语言带来了开销 子程序调用,这阻止了 人从小方法做起。现代面向对象 语言几乎已经消失 进程内调用的开销。

我只是想知道现代面向对象是如何做到这一点的,以及与“旧”方式相比如何?

I'm reading through the Source Making site, specifically the Refactoring section. On the page describing the Long Method problem, the following statement is made:

Older languages carried an overhead in
subroutine calls, which deterred
people from small methods. Modern OO
languages have pretty much eliminated
that overhead for in-process calls.

I'm just wondering how modern OO has done that and how does that compare to the "old" way?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

玉环 2024-08-12 08:48:09

不要相信你读到的所有内容,


我认为你明智地忽略了这一说法。这毫无意义。

说实话,我根本不相信这种说法。所发生的情况是,CPU 的速度变得非常快,实际上比设计那些旧语言时快了一千倍。

程序也变得更加复杂。此时,我们不关心“分支和链接”或任何函数调用机制所涉及的(现在)少量开销。我们有数百万个像素要绘制,或者有一个数据库要访问,或者有一个网络要提供。从某种程度上来说,这些操作是昂贵的。方法调用在噪音中。

在 C 中进行方法调用的开销比在任何现代语言中都要少得多。毕竟,现代语言有一个 CLR 或 JVM 或 Ruby 解释器,它们首先是用 C 编写的。

但没关系。 CPU 的速度足够快,可以将程序推进到下周。重要的是保持软件层层(现在主要是面向对象)正常工作,现代语言帮助我们做到这一点,并且首先使编写变得更容易。

确实,它们更慢,而不是更快,因为这就是我们现在想要的。 3 倍的开销,1000 倍的 CPU 速度,我们仍然以 300 的优势获胜,并且拥有更好的语言。

Don't believe everything you read


I think you are wise to kind of trip over that statement. It makes no sense.

Really, I don't believe that statement at all. What has happened is that the CPU's have become remarkably fast, literally a thousand times faster than they were when those old languages were designed.

Programs have also become more sophisticated. At this point, we don't care about the (now) tiny amount of overhead involved in "branch and link" or whatever the function call mechanism is. We have millions of pixels to paint, or a database to access, or a network to feed. These operations are expensive, in a way. A method call is in the noise.

There is a lot less overhead in making a method call in C than in any modern language. After all, the modern language has a CLR or JVM or Ruby interpreter that is written in C in the first place.

But it doesn't matter. The CPU is fast enough to kick the program into next week. What matters is keeping the layers and layers of (largely now OO) software working correctly, and the modern languages help us do that, as well as make it easier to write in the first place.

Really, they are slower, not faster, because that's how we want it now. 3x the overhead, 1000x the CPU speed, we still win by 300, and have a better language.

半枫 2024-08-12 08:48:09

如果方法很短(并且不是虚拟的),现代编译器实际上会为您内联该方法,这会带来一些性能提升。

除此之外,我认为方法调用并没有变得特别快。但另一方面,计算机却有。在所有情况下(但可能是最极端的情况),专注于可读性是更好的投资,而不是出于性能原因而制作长方法。

Modern compilers will actually inline the method for you if it is short (and not virtual), which gives some performance gain.

Apart from that, I don't think that method calls in particular have become so much faster. But on the other hand the computers have. In all cases (but possibly the most extreme ones), it is a better investment to focus on readability, rather than making long methods for performance reasons.

ペ泪落弦音 2024-08-12 08:48:09

看两句话:

较旧的语言在子例程调用方面有很大的开销,这阻止了人们使用小方法。
现代面向对象语言几乎消除了进程内调用的开销。

您想知道“旧”和“现代”是什么意思,以及哪些语言功能可能会影响性能。

如果他们指的是较旧的语言,例如 Fortran,那么它们就完全错误了 - 旧版本的 Fortran 不支持本地范围,因此子例程调用非常快,因为它们不需要在调用时保存本地变量所保存的值制成。为每个嵌套函数调用保存局部变量的要求在支持递归的语言(例如 C)中构成了开销。 Fortran 的更高版本添加了递归函数,但您必须显式地将函数标记为递归。

所以基本上“旧语言”的开销较小。因此,要么是垃圾,要么他们正在将“现代 OO 语言”与

传统 OO 语言中的“旧 OO 语言”进行比较,例如 Smalltalk(Smalltalk 是最古老的“纯”OO 语言,但这也应用最近受 Smalltalk 启发的语言,例如 Ruby 和 Python;就过程调用而言(Ruby 是非常传统的),每个过程调用都可能是一个多态方法,因此在调用时表现得就像在运行时按名称查找一样。 。在不改变语言的情况下,Smalltalk 的现代实现通过内联重复调用的多态方法运行得更快。

但 Smalltalk 并不是一种现代语言(在很多方面它是 Lisp 的一种方言,因此它的遗产是 20 世纪 50 年代,尽管它是在 1970 年代才出现的),并且改进是针对实现而不是语言。 Sun Java 运行时、Google JavaScript 运行时和许多常见的 lisp 运行时也存在同样的改进; Java 和 JavaScript 都是比 Smalltalk 更新的 OO 语言,但是尽管这些语言具有这些特性,但方法调用优化仍然存在(Java 的静态类型建议使用静态调度表,就像许多 C++ 实现一样,JavaScript 的非常晚的绑定和缺少类使得优化有点难以实现)。

我想不出一种方法来阅读这两个正确的句子。

如果您再看一下文章的更广泛的上下文,这意味着代码风格已经从长方法变为短方法。

那么是否认为这个 Smalltalk 代码来自 197Os 现在将使用更短的代码编写Java 或 C++0x 等更新的 OO 语言中的方法?

enter [ self show. edit Menu show. scrollbar show ]

leave [ document hideselection. editMenu hide. scrollbar hide ]

outside
  [editMenu startup => []
  scrollbar startup => [self showdoc]
  ^false]

pendown [ document pendown ]

keyboard [ document keyboard ]

这似乎也不太可能。 OO 带来了变化,它与 Bee Gees 一样现代。

然而,在过程代码和面向对象代码之间,方法大小和重构为更小的方法发生了很大的变化。

一旦你有了一个对象,其字段保存了你正在使用的数据,那么将该一个对象传递给子过程比传递十几个不同的局部变量要容易得多,这可能是小方法更常见的最有力的原因在传统的面向对象中,但它与性能和现代语言都没有很强的相关性。只是在 C 函数中处理 12 个 double 和 int 参数需要大量工作,而且很容易出错。

我曾经遇到过查看一大段程序代码并创建一个对象的情况(或 C 中的 struct )来保存状态以允许重构,并执行相反的操作(将大量字段复制到局部变量中并手动内联较短的方法)以提高性能(在 Java 1.3 IIRC 中)。由于您可以在 ANSI C 中进行重构,因此它几乎不限于现代 OO 语言。

Looking at the two sentences:

Older languages carried an overhead in subroutine calls, which deterred people from small methods.
Modern OO languages have pretty much eliminated that overhead for in-process calls.

You have wonder what is meant by "Older" and "Modern", and what language features might effect performance.

If they mean older languages, for example Fortran, then they are completely wrong - older versions of Fortran didn't support local extent, so subroutine calls were very fast, as they didn't require saving the values held by local variables when calls were made. The requirement to save local variables for each nested function call constitutes an overhead in languages such as C, which support recursion. Later versions of Fortran add recursive functions, but you have to explicitly mark the functions as recursive.

So basically "older languages" have less of an overhead. So either it's rubbish, or they are comparing "modern OO languages" with "Older OO languages"

In a traditional OO language, such as Smalltalk ( Smalltalk is the oldest 'pure' OO language, but this also applies the recent Smalltalk inspired languages, such as Ruby and Python; as far as procedure calls go Ruby is very traditional ), every procedure call is potentially a polymorphic method so behaves as though it is looked up by name at runtime when the call is made. With no change to the language, modern implementations of Smalltalk run faster by inlining polymorphic methods which are repeatedly invoked.

But Smalltalk isn't a modern language ( in many ways it's a dialect of Lisp, so its heritage is 1950s even though it only arrived in the 1970s ), and the improvement is made to the implementation, not the language. The same improvement exists in the Sun Java runtime, the Google JavaScript runtime and many common lisp runtimes; both Java and JavaScript are a more recent OO languages than Smalltalk, but the method call optimisation exists despite the features of those languages ( Java's static typing would suggest the use of a static dispatch table, like many C++ implementations have, JavaScript's very late binding and lack of classes make the optimisation a bit harder to implement).

I can't think of a way to read the two sentences which is true.

If you then look at the wider context of the article, it implies that the style of code has changed from long methods to short methods.

Is it then arguing that this Smalltalk code from the 197Os would now be written using much shorter methods in a more recent OO language like Java or C++0x?

enter [ self show. edit Menu show. scrollbar show ]

leave [ document hideselection. editMenu hide. scrollbar hide ]

outside
  [editMenu startup => []
  scrollbar startup => [self showdoc]
  ^false]

pendown [ document pendown ]

keyboard [ document keyboard ]

That doesn't seem very likely either. The change came with OO, which is as modern as the Bee Gees.

However, there was a big shift in method size and refactoring into smaller method between procedural to OO code.

Once you've got an object whose fields hold the data you're using it's much easier to pass that one object to a sub-procedure rather than passing a dozen different local variables, and that's probably the strongest reason for small methods being more common in traditional OO, but it's not strongly related to performance nor with modern languages. It's just that it's a lot of work and very error prone to wrangle a 12 double and int arguments in and out of a function in C.

I've been both in the situation of looking at a long chunk of procedural code and creating an object ( or struct in C ) to hold the state to allow it to be refactored, and doing the reverse ( coping lots of fields into local variables and manually inlining shorter methods ) to improve performance ( in Java 1.3 IIRC ). Since you can do that refactoring in ANSI C it's hardly restricted to modern OO languages.

⊕婉儿 2024-08-12 08:48:09

不,仍然有开销。相对于当今的架构而言,它非常小。如果您在 486 上运行代码,则会唱出不同的曲调。

No. There's still an overhead. It's just very small relative to architectures today. If you were running code on a 486, a different tune would be sung.

厌味 2024-08-12 08:48:09

除非您正在编写类似硬件设备驱动程序之类的东西,并且速度必须非常快,否则与替代方案的成本相比,设置堆栈帧和进行函数调用的开销通常是微不足道的。您尝试调试的二十页函数越多,短函数就越成为您的朋友。

Unless you're writing something like a hardware device driver which has to be incredibly fast, the overhead from setting up a stack frame and making a function call is generally something that's trivial compared to the cost of the alternative. The more twenty-page functions you've tried to debug, the more the short function becomes your friend.

狂之美人 2024-08-12 08:48:09

优化当然有帮助,但这里有一个有意的权衡:面对日益增加的复杂性,花费更多的 CPU 周期来提高应用程序质量和可维护性。这是自从第一种高级语言开发出来以来我们一直在做出的权衡。

Optimizations certainly help, but there's an intentional tradeoff here: expend more CPU cycles to improve application quality and maintainability in the face of increasing complexity. This is a tradeoff we've been making ever since the first high-level language was developed.

话少情深 2024-08-12 08:48:09

我认为较新的整体系统受益于以下组合:

  1. 通过堆上的引用传递数据:有效地消除了在堆栈上复制大型对象的需要。
  2. 垃圾收集:通过将程序员从管理对象生命周期的噩梦中拯救出来,使第一点在现实生活中变得有用。例如,在非垃圾收集系统上,从函数调用返回一个对象(实际上只是返回它的引用)将是一场噩梦。

I think newer overall systems benefit from the combination of:

  1. Passing data by reference on the heap: Effectively eliminating the need to copy large objects on stack.
  2. Garbage collection: Making the first point useful in real life by saving the programmer from nightmare of managing object life cycle. For example, returning an object (actually returning just the reference of it) from a function call would be a nightmare on non-garbage-collected systems.
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文