views:

257

answers:

7

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?

+2  A: 

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.

norheim.se
Some runtime compilers inline virtual methods too (they inline the method from the implementing class). They also reverse the inlining if another implementing class loads.
Imagist
In-lining is not a "modern" innovation, this was common even as far back as the late 60's.
RBarryYoung
A: 

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.

John Lockwood
+6  A: 

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.

DigitalRoss
Agreed. For a 1GHz cpu, the 20 words of memory and the instructions to set them up are really very minimal relative to what they are on a 1MHz cpu. If you're writing a very small embedded system, you have to rebalance the equation of course.
Paul Nathan
Admittedly I dont know as much as I should about the code once its ex-complier but I'd always thought we had just as much overhead these days, if not more because the emphasis for much software, and thus languages and toolsets, is more about the return on capital/developer productivity than squeezing out every last drop of performance gain. I appreciate advances have been made but I'd always assumed we were primarily benefiting from the luxuries afforded by Moores Law etc as opposed to anything else.
intermension
Best answer yet.
RBarryYoung
@intermension: and you were entirely right. Good question.
DigitalRoss
About the overhead of a CLR. It is just in time compiler. After the compilation, there isn't much more overhead than with C.
Dykam
JIT is hardly a step up in speed, it's a compromise between interpreted and compiled code. But even if it was just as fast, even if it was completely precompiled, the higher level of abstraction in the modern language still presents more overhead. And that's how we want it, because we get something (a powerful language) for it.
DigitalRoss
JIT *is* compiled code. There is NOTHING interpreted in the CLR, it's just deferring compilation until runtime and when it's compiled, it's compiled, just like C. The only speed difference between JIT-ed C# and statically compiled C is because of the greater maturity of C-compilers and the resource constrained environment a JIT compiler has to work in, not because of some fundamental difference.
JulianR
If that was true, and that was the whole story, then Java and C# would be as fast as C. But they aren't. Look, suppose JIT does work as well, it is a side issue entirely. We are saying primarily that we expect our modern languages to do more work, so we don't care about the overhead of a machine op or a more expensive call. We got on the JIT subject because someone tried to say it was *faster*, sigh.
DigitalRoss
No one claimed it was faster. Really, check again. They are slower because JIT compilers aren't as good yet as static compilers, but not because of some fundamental difference between the two.
JulianR
So much ignorance to compare modern OO compiler, such as g++, being slower than a C compiler, because it's written in C.
Pete Kirkham
@DigitalRoss. C# isn't slow because of the JIT. AOT'd C# isn't faster than JITted C#. Sometimes even slower, as it misses certain platform optimalisations. C# is probably not that much slower than C on for example simple iterations. And speed would be comparable when you do the same amount of OO in C, as the avarage C# code contains.
Dykam
I'm happy to agree that JIT rocks. JIT rules. I do actually like managed code for many reasons. I hardly want to look like I'm disrespecting the technology. My point was something very much different. I mean, I think we *should* be using managed code, and modern OO languages.
DigitalRoss
+1  A: 

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.

Paul Nathan
A: 

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.

Dave
A: 

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.
Hemant
+2  A: 

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.

Pete Kirkham