Economy Of Execution

Runtime performance is one of many desirable properties of a computer language. How fast will programs written in the language run? This page explores two assertions about language design with respect to economy of execution...


Typical examples of object-oriented languages that perform well are C++ and Ada'95 where one can have highly flexible structures (templates, parameterized classes) without paying in execution performance.

On the other end you have Java , Smalltalk, Python. These languages defer type based decisions until runtime. Although just in time compilers are supposed to fix this issue in theory, in practice it doesn't quite happen.

Statement level performance varies over many orders of magnitude from VHDL (which compiles to hardware) to shell script (that forks a task to add two numbers). Performance can be so important that people will even argue over rather small increments as in the discussion on this page.

EconomyOfExecution is growing less and less important (slowly, of course, and there are admittedly exceptions). More important is EconomyOfImplementation?, something that is quaintly overlooked in the paper BadEngineeringPropertiesOfOoLanguages.


My experience, after 20 years or so of application development experience in Smalltalk, C-based OO languages (C++, Objective-C, ObjectC, etc), Java, and so on, is that the claimed performance advantages of "strict static type checking" are less compelling when deployed than those cited here. Here's why:

  1. Static typing often just moves the performance problem. While it's sometimes (but not always) true that a good C++ compiler will outperform a bad Smalltalk implementation on a microscopic benchmark (like adding two floats), this approach usually does not measure the performance impact resulting from the often tortuous path the programmer must follow to ensure that the arguments, as delivered, really ARE floats. Try writing a complex number package in java as an exercise. Measure its performance (ComplexNumberTest). Write the same package in Smalltalk. Measure its performance. Ask the best Java, C++ and Smalltalk programmers you know to do the same. Compare their results. Please bear in mind that if I need fast complex numbers in Smalltalk, I'll probably write and install primitives written in C (NOT C++). The net result will be that the "performance critical" parts will be quite similar, and the "glue" around it will be what differentiates the C++ from the Smalltalk. Static typing won't make that glue go any faster AT ALL -- in fact, it might even slow it down (see 3 below).

  2. Static typed compilers often ignore or can't use the static typing information any more effectively than their loosely-typed (or untyped) counterparts. Much of the alleged benefits of static typing are not used at all by real compilers solving real problems. Try compiling your favorite piece of real C++ code and measuring its performance with optimizing turned off. Use conventional coding tactics to improve its performance by hand until you can't make it go any faster (a factor of two is usually easy for most unoptimized working code). Now turn on the compiler optimization and measure the improvement. Write Smalltalk that does the same thing, and optimize the Smalltalk. Ask the best Smalltalker you know to do the same. Compare the results.

  3. I can write fast code too if it doesn't have to work. Static typed compilers (especially C++) often force the programmer into such convoluted code that typical problems (especially in environments where the requirements are always changing) either cannot be solved at all or where the working code is painfully slow, so slow that any performance benefits gained from static typing are totally swamped by the losses resulting from the confused and confusing code.

The commercial Smalltalk market, as small as it is, exists (especially in the financial and insurance domains) because a huge portion of the C++ development projects died after 24-36 months, millions of dollars expended, and with no working results at all. Ask BankersTrust? about LS1, their first effort to write a commercial Loan Syndication system. Ask Fidelity about its C++ projects. Ask ex-Easel folks about Easel Workbench II (the follow-on to Easel Workbench). Smalltalk succeeded in these environments because shops were able to get something running quickly and were then able to speed up the slow parts, based on real measurements of real problems.

1. Whoa, cowboy. If you declare the parameter as a float, the parameter is bloody hell a float. What you're complaining about is why DefensiveProgramming is stupid. No kidding, eh. On the other hand, if you are talking about DaveUngar?'s amazing type pipelining system, well, that's DaveUngar? and he's amazing. You won't find that in Smalltalk.

When "the parameter is bloody hell a float", then I have to write and maintain other code to handle the three other types it might be -- not because of DefensiveProgramming, but because the parameter can legitimately be one of four types. And I also have to write and maintain the code that tests and dispatches to the right place based on the type. The WORST offender for this is Java, but see below. In the complex number example I cited above, what happens to your code when the complex numbers can have coefficients of each numeric type?

2. You are flat out wrong. Trust me. I ZenSlap the compiler every day with casting.

So do I whenever I have to use a strongly-typed language. That's my point.

3. Yes, this is the whole argument in favour of dynamic typing over static typing. Yawn. A better answer is go sideways in the discussion and AlternateHardAndSoftLayers, or do what we did and write a CodeGenerator in Perl that emits optimized C++ by extending the C++ syntax. -- SunirShah

Agreed, AlternateHardAndSoftLayers is my favorite approach.

P.S. Don't ever again use Java as an example of anything except poor design. Why not go whole hog and use VisualBasic as an example of a dynamically typed language?
Also agreed, about both Java and VisualBasic. -- TomStambaugh

I partially subscribe to your view, although I'm no Smalltalk fan, and I wished I was doing C++ rather than Java at this time. I think that LucaCardelli points out that C++ fares pretty badly in areas other than EconomyOfExecution. But the fact is EconomyOfExecution is a problem of Smalltalk and other OO languages (liek Java), whether you like it or not. What you're missing here is that not everybody does banking applications, and the fact that Smalltalk is good enough in certain domains is more related to the fact that EconomyOfExecution is less important there. Try to implement encryption or any other performance critical algorithms in Smalltalk, those are real problems to be solved too. Well I did it in Java and wasn't happy at all, I take the liberty to presume Smalltalk is similar.

On the other hand, I don't think there's enough convincing evidence of a corelation between the EconomyOfExecution and either EconomyOfSmallScaleDevelopment? or EconomyOfLargeScaleDevelopment? (the two notions Cardleli uses for EconomyOfImplementation?). What you mentioned here is anecdotical evidence. The reason for BadEngineeringPropertiesOfOoLanguages was to call for more research and implementations towards better languages, not to set existing ones in stone. I don't think that adopting a "Smalltalk is all we need and all there ever needs to be" approach is particularly helpful or justified. The reason for this page wasn't to bash Smalltalk nor to glorify C++. -- CostinCozianu

Regarding anecdotal evidence: Take a second read of the article. It's one big anecdote.


Mostly type information is used for instruction selection. C++ can make use of a floating point register for a temp because it knows the temp will always be a float. Smalltalk moves the temp data back and forth from memory. (Some lisps select instructions just as well but they have been rejected for reasons discussed elsewhere. See WhyWeHateLisp) Type information can also improve performance because...

Slower languages exist because they excell in other dimentions such as ease of use. As GeraldWeinberg said: "I can ALWAYS build faster code if it doesn't have to work." There are definite trade-offs with StaticTyping that cannot be ignored in the real world.

There are different trade-offs that various statically-typed languages took, Java being a prime example. For example the project disasters related to C++ are mainly due to the liberal use of pointers and memory management, which are related to EconomyOfExecution, but are not related to StaticTyping.

But it is still unclear if there's anything inherent to StaticTyping that prevents whatever desirable properties you see in dynamically typed languages. Languages such as ML and its variants tend to prove the opposite.

Java has static type checking but JavaTypingWasSimple. This has all kind of benefits, but it also leads to loss of typing (ask yourself how many times have you used the cast operator lately) and consequently it hurts EconomyOfExecution because all kinds of checkings have to happen at run-time and because the optimizer (even if it is inside the just in time compiler/HotSpot ) is prevented from doing its job well.

A cast operator is part of every static-typed language I'm aware of (even Eiffel has one), and is one of the reasons why so few commercial static-typed language compilers actually use the static typing information for anything useful. The presence of the cast operator means that runtime checks have to happen anyway, and the runtime checks of C++ or Java are not inherently any faster than those of Smalltalk. -- TomStambaugh

The fact that there is a cast operator, doesn't mean you have to use it. In Java, yes, you really have to. But in C++ you have to deal with exceptionally rare situations to have to actually use a dynamic_cast, some authors even question the usefulness of the dynamic_cast operator. The same applies to Ada and many other languages.

Let me beat the carcass of this dead horse a little more. The fact that there is a cast operator means that the compiler has to take it into account, whether or not the developer uses it. Thus, the compiler has to emit code that does something predictable at, for example, code position "A" (such as an assignment) when an incorrect casts occur at code position "B" (such as inside an independently compiled module). The effort to reliably sort out what can and can't occur at runtime causes most commercial vendors to walk away from the whole problem. The result is that the overall EconomyOfExecution for real applications that work is about the same for modern commercial implementations of Smalltalk, C++, and Java. -- TomStambaugh

Tom, I think the burden of proof is on you. Come up with a practical example, cite a relevant study or something, but don't just state something you believe to be true, while it's common sense that it isn't. C++ compiler doesn't insert anything at point A to catch a bad cast at pont B, on the contrary, the most likely behaviour in such a situation is a total program crash. The only' thing that might slow down a C++ program is setting up exception traps, which can be disabled anyway, by a simple compiler switch. There's also the problem of object allocation and garbage collection, and tons and tons of other things - like the 23 machine instructions that I got in ComplexNumberTest. Can you back your claim with some real evidence ? Just spear us of the real applications that work. I've done a few real applications that work in C, C++, DelphiLanguage and BorlandPascal?, developing in any of these languages (without garbage collection, dynamic typing and the goodies of Smalltalk) ain't the nightmare that smalltalkers would like us to believe it is. On the contrary, it is very enjoyable. -- CC

Excuse me, Costin, but I'm responding to two assertions, from above:

* optimizers work better if they have more information.
* run-time checks can be replaced by compile-time proofs.

If anything is to be "proven", it is these assertions, together with the commonly asserted (but in my experience mythical) implication "Applications written in static typed languages are faster". I will not fall into the semantic trap of attempting to prove a negative.

Tom, what you call assertions are common places in ComputerScience. There's no point in debating these with you unless you come up with new evidence or new ideas. Who's talking of proving the negative ? What is your experience, what have you measured ?

I don't doubt that you have written real applications in C, C++, DelphiLanguage, and BorlandPascal?. So have I. The claim I dispute is YOUR implied claim that those applications would have had worse performance had they been written in Smalltalk (I agree with you that Java would have KILLED them... :)). If the strongly typed compiler of your choice in a language that includes casts really DOES blindly execute an operation without checking types as you argue it should, I argue that for an application of any complexity, you will see a large amount of unexplained, apparently random application crashes -- precisely the reliability problem that strong-typing is alleged to "solve". And if you ALSO skipped the garbage collection, I'll bet your applications also had core leaks and a whole different set of apparently random failures caused by dereferencing stale pointers created the effort to avoid the "overhead" of the garbage collector. If you REALLY mean that you ship code with BOTH unchecked casts AND manual storage management, I would respond that you make my argument for me more effectively than I can make it myself. tms

You can dispute no matter how much you want, but please try to come up with 23 machine instructions in ComplexNumberTest.

I agree that we should continue this discussion in ComplexNumberTest. Please see my additions to that page. -- TomStambaugh

And about my compiler, it doesn't execute blindly, it proves at compile time, because you usually don't use dynamic_cast in C++. Even in case you do use dynamic_cast, only yhen it inserts a check at runtime. Let's get serious and delete this crap, shall we ?

Using dynamic_cast<> whenever I might be tempted to use a C-style cast was ingrained into me by days upon days of wasted time debugging what turned out to be "slicing" problems on a largish C++/MFC project. I find it hard to believe that the contributors on this page who speak from experience with C++ won't at least acknowledge that C-style casting can lead the unsuspecting OO programmer into serious trouble.

A C-style cast has the effect of adding an axiom to the compiler's "proof" system. If you add a false axiom, then you should expect to get false conclusions. DontDoThat!


Tom, I respect your opinion in matters of Smalltalk usually, but you are really wanking it here. Most Smalltalks are one tenth the speed of C. DaveUngar?, because he's amazing, pushed Self to one half the speed of C years and years ago. Worse, C++ is faster than C because it has more tricks to pull. And your whole claim that casting costs in C++ is totally bogus. I know how much a cast costs because I spend all day measuring the impact of various C++ expressions against a variety of compilers on a variety of platforms using a variety of processors. The only thing you have to worry about when casting is integer extension and calls to emulated 32-bit multiplies and divides. Unless you are using dynamic_cast<>, but dynamic_cast<> is evil. -- SunirShah

Let's continue this discussion at ComplexNumberTest, and focus it on real stuff. -- TomStambaugh


I spend all day measuring the impact of various C++ expressions...

That leave you much time for implementing things with BusinessValue? ;)


Somebody asserted during a refactoring of this page that: Object-oriented principles run counter to performance.

It wasn't clear if the statement itself was asserted, or if the assertion was that the discussion on this page was about that. I believe there's no evidence to affirm that "Object-oriented principles run counter to performance". Please clarify.

While I didn't say that, it's definitely true. Objects boil down to extra level of indirection. Every level of indirection slows you down. -- SunirShah (unless it's resolved at compile-time, as for C++ templates -- DaveWhipp).

And speeds you up as well - SlowDownToSpeedUp.


CategoryCpp


EditText of this page (last edited February 27, 2010) or FindPage with title or text search