Premature Optimization? 33
I have a theory.
It’s one of those theories that I don’t want to get ruined with actual facts, but I suppose I’ll put it out there and see what I learn.
The theory stems from a series of observations about the way some Microsoft development shops write code versus others. I’ve noticed that some shops like to make many things sealed (final). I’m not talking about the occasional class, but rather the default is to use sealed on most things. In addition to classes being sealed, there’s a heavy bias against virtual methods. I’ve not noticed this with Java development groups, though in the spirit of honest disclosure, I’ve only noticed thins tendency is about 50% of the MS shops I’ve visited – and it seems like the tendency is reducing.
- It expresses the design intent
- It’s for performance
- The code as written cannot be safely overridden.
(Before I go any further, I am not arguing against the use of sealed or final. I will say that in my practice, it has to be justified to be used rather than the other way around.)
Back to the reasons. I generally don’t buy these reasons. Each may be perfectly valid but none of them stand on their own; each requires further discussion.
I’ve had those discussions with various people and most of the time they really don’t hold water. Here are some quip-y responses to each of them:- It expresses design intent – you don’t know how to design
- It’s for performance – do you have any idea how a VM works?
- The code as written cannot be safely overridden – So the code is so badly written that nobody understands it?
Again, let me be clear, there are good reasons to seal things. My point is that while there are good reasons, most of the time I’ve seen the habit is a form of cargo culting; monkey see, monkey do.
This observation sat around for some time until I read some stuff in a book called CLR via C# (good reading if you want to better understand the CLR, and if you’re developing code for a virtual machine, I think it’s a good idea to understand the virtual machine at least a little).
Reading that book and my observations led to my theory:People are sealing as much as they are because virtual table binding is done way to early.
The thing I read in the book I mentioned above relates to when virtual methods are bound. This applies to 2.0 version of the CLR, but I don’t think there’s any change for the latest version of the CLR and this seems like a pretty “hard” design decision. This book mentions that binding of virtual methods is performed at compile time.
I’m not making a distinction between compile time and link time for this discussion, but I’ll be a little more precise. The generated code stored in an assembly “knows” which virtual function it is invoking because in the generated byte code is an index into a virtual table.
How is this a problem?
I write a base class and ship it. You write a derived class (OK, maybe you use delegation, good for you, but the problem still remains). Your coupling to my class involves using a method with the virtual keyword (Java programmers, you don’t have to do this, regular methods [not static, not a constructor, ...] have the potential for dynamic dispatch unless you declare them final). At compile time, your object module (which eventually resides in an assembly that will get loaded and executed some time later) knows that it wants to call the 3rd virtual method in the v-table.
So far, so good.
Next, I add a new virtual method to my class and ship a new assembly.
Question. To use this new assembly, do you need to recompile?
Yes.
What?
Yes. What happens if my newly added method is the first virtual method? Then your compiled code that is happily living in a peaceful assembly somewhere has a stale reference. It thinks it wants to run the 3rd method in the virtual table but now it really should be using the 4th virtual method.
This is similar to a caching issue or premature optimization. The calling code has a compile-time generated index into the v-table. Why is this? Is it for performance? Not a good argument, this pre-calculation is irrelevant with modern JIT compiling techniques. I think the reason it is done this way is that C++ does things this way. C++ needs to because of its execution model (there’s not virtual machine between the code and its execution).
No big deal, you think, I’ll just recompile my code.
That will fix the problem. Or will it?
Say you’re stuck using an old version of some assembly that uses an old version of another assembly that you also use. Do you recompile both your code and that other assembly? You’ll have to but you might forget. Or you might not be able to.
.Net handles this by having an excellent dependency system to track which versions of which assemblies work with with other versions of assemblies. They need this to address problems introduced by the decision to perform such early binding.
Simply put, this is an unfortunate decision that I believe was more based on precedence or history than on sound design decisions.
This is not what happens in the Java Virtual Machine (JVM).
In the JVM, the binding is done later—much later. It is possible that my class got loaded and JIT’ed so that all of the methods that could be virtually invoked (not final) are in fact not virtually invoked. So far, no subclasses have been loaded, so there’s been no need. After my system has been running for a few weeks, a new class (a subclass) gets loaded and invalidates the JIT’d version of my class. No problem, the system will dump the old version and JIT it again, on the fly.
Even if there is a need for virtual dispatch, it might be that most of the time I use one version of method for a particularly heavily used subclass. The JVM might inline the common case and virtually dispatch the others.
Bottom line: what’s being done with JVM’s is indistinguishable from black magic for most of us.
I can use new JAR files and not have to worry about recompiling to remove stale v-table references. The method binding will just work. (This does not remove all problems with changing versions of JAR’s but it gets rid of one persnickety one.)
But it goes deeper. As I mentioned above, I’ve noticed several Microsoft shops that use a lot of sealed classes or avoid using virtual methods because “they perform poorly”.
OK, let’s talk about method invocation for just a second (or make that pico second). On my machine, I timed virtual method dispatch. I should check my numbers, but what I got was that virtual method dispatch was taking around 450 pico-seconds. So you can invoke roughly 2.2 Billion methods per second.
So:- Virtual method dispatch is not that expensive
- The JVM can address caching issues with code analysis and inlining
- You only pay the price if your design requires it
- The people writing the JVM are better and micro optimization than certainly me, and I’d say most people
- Testability
- Maintainability
- Flexibility
- Extensibility
Look up the percentage of time spent “building” a system versus “maintaining” a system. The percentage has been on the rise, and it’s 80 – 95% typically. 80% of the cost of development is spent maintaining code. (I believe a lot of this percentage has to do with the definition of “done†– but that’s a whole other discussion).
You might think that locking things down make them more stable and that stability leads to maintainability.
That’s not really the case.
Things change. Requirements change.
Locking down a class to keep it from breaking is like signing off on requirements to keep them from changing. Neither thing is really a good idea.
I CAN lock down my classes in a sense. I can use tests to describe my assumptions so the class is locked down so long as we all agree that we’ll keep the tests passing. That is a fine level of granularity.
But I digress.
Back to the whole sealed thing. I was wondering why so many places that use Microsoft stuff seal their classes and do not use virtual methods (in my experience, it’s about 50%). I then read that thing about the binding of virtual methods at compile time. And then about 2 weeks later it hit me.
If you use virtual methods and you have a messed up build system, then you’ll get strange behavior. Sealing things makes those surprising behaviors go away, so seal stuff.
It makes sense. This is something that C++ was notorious about. You hoped you’d get a segmentation violation but often the wrong method could get called and the system continued to run…
Unfortunately, the “solution” – sealing – is attacking a symptom, not the actual problem. This is where following the 5-why’s of Toyota would have been a good idea.
“Seal your classes” Why?- Because they will be more stable. More safe/it expresses my design. Why?
- Because my design says that this should not change. Why?
- Because when things change, sometimes “bad things happen.” Why?
- I don’t know…
And right there is where you know the solution is a bad idea.
In reality I suspect the habit perpetuates because once something appears to work, it is cut and pasted ad nauseam.
Should you never seal things?
Never say never.
Or better yet, never say “never, never, never.”
There are three levels of never:- Never: Don’t do it because you may not know what you are doing. (Once you know the rules, you can do it.)
- Never, Never: You know what you’re doing most of the time, you are doing a bad thing but you really do know better.
- Never, Never, Never: DON’T DO IT!
There are few things that are Never, Never, Never. Dereferencing NULL comes to mind (but then maybe I waned a core dump if I get to this point in the code).
I’d place using sealed/final in the Never, Never camp if I’m using tests to describe the semantics of my classes. OR, if I’m writing concurrent code, using final on fields provides certain guarantees…
However, let’s say that I’ve been told not to use tests (I’ve seen it at more than one place). Then that “never, never” becomes a “sure do it to cover my assâ€. Someone can’t subclass and mess things up. Someone can’t forget to compile their code when mine changes and cause their code to break…
Now is this a reason to avoid using Microsoft products? No. I really do like C# (for a statically-typed language that is). And I’m convinced that Java is a better language because of the work done by and in C#. And let’s face it, having more VM’s is a good thing. It improves competition. It raises the level at which we can expect to work.
But to quote Tim Booth/James in a rather trite way, “there’s a chain of consequence within, without.” That one thing, binding to the v-table so early, has caused a chain of events that leads to a platform that seems a little bit more sluggish to develop in to me. (I expect flames for this.)
I think this design decision is a reflection of a mind-set that permeates the development environment. For example, if I have a large solution with several projects, it sure seems that dependency checking in the build environment takes a long time. When I watch Developer Studio build things, it looks like make is running under the covers and performing a bunch of file checks to see what has changed. Where’s the incremental compilation?
I’ve seen very good work in both .Net and Java development efforts. It just seems that the frequency of unnecessary, basic kinds of environmental design debt occur more frequently in a .NET development effort than in similar Java development efforts.
Maybe it’s a sampling effect. To paraphrase Weinberg, “there’s always a sample, be aware of it, you can’t remove it.” I’m a consultant, I go places where they need consultants. I never, never go places that don’t need consultants, so my sample set is biased (I have one experience, maybe two, where I went and the people did not need consultants because they were doing fine – or maybe they didn’t need technically-oriented consultants).
So what do you think?- Is using sealed by default a good design choice?
- Is the binding of methods early a good thing?
- Was the decision by design or because of history?
- Has/will the CLR change?
1) No. It makes things a pain in the neck to test. :) 2) Late binding is better. I believe that the decision is by design because MS doesn’t really know anything. :) 3) Yes. The CLR (and MS) will eventually catch up to what I would consider best practice in the VM/Language arena. .NET has done a good job of learning from “more mature languages” and will learn a couple of things of it’s own moving forward.
Great post. :-)
While I’m a big fan of your condescending tone, I’d say performance is the main reason you see sealed being so prevalent. (both from folks that do and don’t understand how a vm works…the latter camp would probably ask if you understand the difference between what gets jitted for a virtual method on a sealed vs unsealed class, for instance)
Stay edgy.
Performance. Heh.
The majority of shops manage performance by 1) collecting old wives’ tales, guesses, and out-of-context rules of thumb and 2) enforcing them as “standards”.
It’s the rare shop indeed who manages performance by measurement and investigation. More common is the trial-and-error optimization where they one who speeds up the process doesn’t really know why the new way is faster (whether?) and adds to the gathered lore about performance out of ignorance.
It’s okay to be condescending when dealing with unreasonable people.
Great post! I’ve been saying this, or something very similar to this for a while now, but not nearly as well as you’ve done here.
But for my answers:
Great post! I’ve been saying this, or something very similar to this for a while now, but not nearly as well as you’ve done here.
But for my answers:
This post is incorrect in the way that virtual method dispatch works. I’ll see if I can get a blog post up with the details shortly.
Evan,
This is more about when binding occurs, not method dispatch. Having said that, I’ll be interested in your blog (please post a link!).
I’ve wondered if I understand the binding. I read about the virtual method bidning in a book called the CLR via C# and when do you get your post up, could you do the following:I’d like to know more about this.
Also, if you know about the class-loading mechanism of .NET, can you answer the following questions?Thanks,
Brett
Well, first off adding a new virtual method does not require a recompile. The slots which are stored in the meta data aren’t just an array with indexes – they’re just data saying this method overrides that method. You’ll never end up calling the wrong virtual method on the CLR – if you could do that you could violate type safety and all of the CLR’s security system would be broken. So that’s a non-problem. Java does certainly do a (better) job of inlining virtual method call and then removing that optimization if a subclass gets loaded which overrides the virtual method. The CLR could do the exact same thing if they wanted to. So yes non-overridden virtual methods perform better on the JVM. That’s probably partially because the JVM defaults to having everything be virtual and therefore it’s a more critical optimization for them.
Ok, so why does everyone have sealed classes and why do methods default to being sealed? He lists “maintainability†as a reason to keep virtual methods – but that’s the reason not to make everything virtual. When you make a method virtual you’re declaring that you can override that one method and still get correct results from the base class. You’re introducing a much more difficult contract that you need to fulfill. And you’re limiting the way you can evolve the class in the future – after all that virtual method that was overridden needs to be called at the same time it always was.
Let’s consider a worse class scenario. Let’s say that ArrayList (whose methods are all virtual) came up with a faster method of doing additions. Let’s say in V1 ArrayList.Add was implemented by calling Insert(). Let’s say in v2 they wanted to optimize Add so that it would just do the add directly. I subclassed ArrayList and only overrode Insert because it seemed to catch everything. Now I upgrade an my Insert doesn’t get called so my code has been broken.
Can this be handled? Yes, but the relationship between every single virtual method needs to be defined as part of a contract of the class. Furthermore tests need to be wrttin which verify all of those relationships. You can even imagine turning these methods into something which are “stateful†– and now the order they get called in becomes significant not just which ones get called.
Defining the contract of the class that strictly means that you’re that much less free to change the implementation in the future. And one of the main points of object orientated programming is to encapsulate the internal implementation details so that you’re free to change them in the future. So rather than “pre-mature optimization†I would call this “pre-mature extensibilityâ€. Does someone really need the capability to override every method on ArrayList? If they really need that custom of a list why not just implement IList?
I don’t have any evidence to back this up, but I seriously doubt that adding a new virtual method could ever result in the wrong method being called by a so-called “stale” caller (one that hasn’t been recompiled). That would certainly be a fly in the ointment.
Also, I agree that virtaul-methods-by-default is premature extensibility. If you need testability (mockability?), then you should probably be asking for interfaces rather than virtual-by-default methods.
I believe that this subject is discussed in the Framework Design Guidelines. The reason given for sealing classes and final-by-default is that the classes/methods can always be unsealed or virtualized in a future version without a breaking change, but you can never go backwards to sealed/final without potentially breaking existing code. It is similar to the reason that classes and class members without an explicit visibility modifier are considered private by default – you can always increase the visibility later, but you can never decrease it w/o accepting the breaking change.
Oh gawds, so why does anyone use C# or .Net then? Is there CLR THAT stupid it can’t properly inline or handle those calls efficiently? Someone else mentioned the .NET CLR is pretty dumb. This is like, so , 25 years ago.
thanks, very nice post!
Your example is at least a little bit flawed.
Given the modifications to the base class i’m going to make the bold assumption that the base class doesn’t guarantee that add is implemented in terms of insert. Ergo by assuming this you are in effect adding an additional precondition to your class. LSP says this is a no no. No valid subclass can have preconditions stronger than their super class.
Having said that i’m sure people do this all the time(i’m sure i’ve done it personally) however this doesn’t mean that virtual is to blame. This means that unwisely implementing only part of the original spec is to blame.
The assertion that is made by java and most of the agile/tdd/refactoring crowd is the opposite. Can you say for certain that there is absolutely no valid reason whatsoever for someone to override a given method of ArrayList?
If you can’t then you should make it virtual because there may come a time that that reason occurs and they only have binaries remaining.
And remember that when all else fails, extending and supressing used behaviour is a workable unit testing technique. Especially when no interface exists or is unavailable because the implementation is being referenced explicitly (you can’t use an IList if the function requires an ArrayList).
Well,
we want to do things with your ideas you never thought of. I like the idea of never ending creativity. Thats why I don´t finalize my code if I don´t have to.
Java supports me very well in doing that.
Great post! Nice balance between biased experience and hard facts. It enriched me…
a JavaDev
Really useful post!
Great post, thanks!
Please remove this blog post. It is full of disinformation that will lead curious people astray.
All of your assertions about method binding and virtual tables are pure fiction. It is obvious that you have never actually looked at compiled IL. IL actually stores the name of the method; it doesn’t even matter whether the method is virtual or not. All of that is resolved at JIT time. Which means your original assertion – that you have to recompile to use a class that has added a virtual method – is patently false.
As for the rest of the post, there is a lot of debate on both sides, and there are certainly advantages to not sealing classes and methods. However, in my experience they are far outweighed by the disadvantages. Your post does nothing to actually address these disadvantages. This has nothing to do with paternalism. The most significant disadvantage is that it limits extensibility. Don’t believe me? Check out http://blogs.msdn.com/ricom/archive/2005/08/26/performance-quiz-7-generics-improvements-and-costs-solution.aspx, specifically the end of Q3:
“Why didn’t we fix [performance problems in] ArrayList while we were at it? Ugh… we can’t. If we “fixed it” we would break existing applications. Because the class isn’t sealed it’s not possible for us to change the virtualness of the key functions involved so we have to live with that mistake.”
thank you for sharing the post
Some professions are easier to make wk zed; some professions are good at fighting; o, how about Sword Main? Is it useful in making WonderKing gold, its strong power could help you make WonderKing Zed. And if you also think so, wk gold and go into this game to enjoy it and the zed.
Crown Holder Jeanslaunched as a diffusion line in 1981. Internationally renowned and oozing with quality and style, Cheap Crown Holder Jeans takes contemporary fashion to another level.From elegant and glamorous backless ball dresses to casual basics, Crown Holders Jeans dominates the world of designer fashion for the modern man.Mens Diesel Jeans is an Italian design company known best for its clothing which is aimed mainly to the adult market, especially the jeans. The new season Cheap Diesel Jeansare definitely the must buy pair of jeans this year. They incorporate a Straight cut design which are very casual and would compliment a smooth shirt and a pair of your favourite shoes tremendously well.Dolce Gabbana Jeans can be categorized under designer or casual wears and people of all age are overtly crazy about them. Color, texture, cut or wash- everything blends enormously well with the new-age fashion trend as well as the comfort quotient of a working man or woman. Teenagers and youngsters too can die for the Dolce And Gabbana MensJeans.
It seems unlikely that new Slim features will be needed very often.
With Monster Beats Studio, you’ll hear every sonic detail the musicians and recording engineers laid down in the studio. Deep bass. Hard hitting drums. Bold midrange. And clean, undistorted highs. All in a comfortable fit you can wear for hours. Solos are compact and incredibly lightweight and ready for your active lifestyle, gym work out, or frequent travel. The Monster Dr Dre Headphones design allows Solos to fit into the included compact carrying case. With Dr Dre Earphones getting a great music experience has never been easier. Now, a new headphone in the Beats tradition, Beats By Dr Dre in ear brings you amazing sound in a smaller, lighter on-ear design.
But to really see the difference between these cameras I put together an overview containing 100% crops. It’s not fair comparing the white iPhone 4 with an 18 megapixel DSLR but it’s good to have as a reference.
I am really appreacite!
I am really appreacite!
I am really appreacite!
Keep your Contacts and SMS safe! Actually, the contacts and SMS have more values than a cell phone’s own value. You can pay money to buy a new iPhone, but cannot buy your lost contacts and SMS back. So it’s important for you to backup your contacts and SMS in iPhone. And we recommend you backup contacts and SMS regularly. Our backup software can help you take a snapshot for your contacts and SMS. Your important personal information will be never lost.
Would you like to banckup iphone SMS to mac, macBook, macbookPro as .txt files? Now a software iphone SMS to Mac Backup can help you to realize it.
your website shows me some useful information,thanks for sharing.
interesting article
internette görüntülü olarak okey oyunu oyna, gerçek kisilerle tanis, turnuva heyecanini yasa.
ok i love
Slewing bearing called slewing ring bearings, is a comprehensive load to bear a large bearing, can bear large axial, radial load and overturning moment.
Son PDG Mark Parker a déclaré: «Nous allons continuer à réaliser des chaussures solides et des vêtements de la croissance des ventes pas cher nike air max Tn , qui me dit que la classe moyenne en pleine expansion dans les marchés émergents, en augmentant leur pouvoir d’achat, les consommateurs se sentent bien partout, ils sont prêts à dépenser de l’argent dans ce lieu. ” Janvier prochain, prendre des dispositions pour la livraison de chaussures air max tn et la demande du marché des vêtements en Chine ont augmenté de 27%, d’autres marchés émergents a augmenté de 22%. Analyste Matt Arnold, a déclaré: «Toutes les indications montrent que de nombreuses entreprises de vente au détail où le monde est la croissance des revenus très faibles, la air max 90s continuera de maintenir la dynamique de croissance forte.”