views:

2366

answers:

12

I have come across a lot of optimization tips which say that you should mark your classes as sealed to get extra performance benefits.

I ran some tests to check the performance differential and found none. Am I doing something wrong? Am I missing the case where sealed classes will give better results?

Has anyone run tests and seen a difference?

Help me learn :)

A: 

Well yes, that is the functional purpose of marking a class sealed. But I have read that there is a performance benefit to them as well, and that is what I have been trying to explore.

Vaibhav
you should probably comment on answers instead of posting more answers to your own question, since voting can change the order of presentation
Steven A. Lowe
+9  A: 

The JITter will sometimes use non-virtual calls to methods in sealed classes since there is no way they can be extended further.

There are complex rules regarding calling type, virtual/nonvirtual, and I don't know them all so I can't really outline them for you, but if you google for sealed classes and virtual methods you might find some articles on the topic.

Note that any kind of performance benefit you would obtain from this level of optimization should be regarded as last-resort, always optimize on the algorithmic level before you optimize on the code-level.

Here's one link mentioning this: Rambling on the sealed keyword

Lasse V. Karlsen
the 'rambling' link is interesting in that it sounds like technical goodness but is in fact nonsense. Read the comments on the article for more info. Summary: the 3 reasons given are Versioning, Performance, and Security/Predictability - [see next comment]
Steven A. Lowe
[continued] Versioning only applies when there are no subclasses, duh, but extend this argument to every class and suddenly you have no inheritance and guess what, the language is no longer object-oriented (but merely object-based)! [see next]
Steven A. Lowe
[continued] the performance example is a joke: optimizing a virtual method call; why would a sealed class have a virtual method in the first place since it cannot be subclassed? Finally, the Security/Predictability argument is clearly fatuous: 'you cannot use it so it's secure/predictable'. LOL!
Steven A. Lowe
Did I mention that I loathe sealed classes? There's nothing worse than getting 95% done with an application and discovering that the one feature the customer wants that the library doesn't provide logically belongs in a subclass of a sealed base class. Grrrrr!
Steven A. Lowe
@Steven A. Lowe - I think what Jeffrey Richter was trying to say in a slightly roundabout way is that if you leave your class unsealed you need to think about how derived classes can/will use it, and if you don't have the time or inclination to do this properly, then seal it as it's less likely to cause breaking changes in others' code in future. That's not nonsense at all, it's good common sense.
Greg Beech
A sealed class might have a virtual method since it might derive from a class that declares it. When you then, later, declare a variable of the sealed descendant class, and call that method, the compiler might emit a direct call to the known implementation, since it knows there is no way that this might be different than what's in the known-at-compile-time vtable for that class. As for sealed/unsealed, that's a different discussion, and I agree with the reasons for making classes sealed by default.
Lasse V. Karlsen
+1  A: 

Sealed classes should provide a performance improvement. Since a sealed class cannot be derived, any virtual members can be turned into non-virtual members.

Of course, we're talking really small gains. I wouldn't mark a class as sealed just to get a performance improvement unless profiling revealed it to be a problem.

Karl Seguin
A: 

@lassevk Yup, I googled and googled... And what you mentioned is exactly what I got. Nowhere have I found an exact answer which says in which situations something like this can be helpful.

I mean, I did try creating class structures with inheritance and virtual methods, and all that. But, still not difference in performance.

So, either I discard the claim I see at so many places that this optimized performance (which I don't want to do since it has been made by people smarter than I am), or I search till my thirsty soul is quenched by knowledge.

So, the question is still open. Has anyone worked numbers on this? And does anyone know what are the exact situations where this applies?

Thanks.

Vaibhav
A: 

@Vaibhav, what kind of tests did you execute to measure performance?

I guess one would have to use Rotor and to drill into CLI and understand how a sealed class would improve performance.

SSCLI (Rotor)
SSCLI: Shared Source Common Language Infrastructure

The Common Language Infrastructure (CLI) is the ECMA standard that describes the core of the .NET Framework. The Shared Source CLI (SSCLI), also known as Rotor, is a compressed archive of the source code to a working implementation of the ECMA CLI and the ECMA C# language specification, technologies at the heart of Microsoft’s .NET architecture.

hitec
A: 

The test included creating a class hierarchy, with some methods which were doing dummy work (string manipulation mostly). Some of these methods were virtual. They were calling each other here and there.

Then calling these methods 100, 10000, and 100000 times... and measuring the time elapsed.

Then running these after marking the classes as sealed. and measuring again.

No difference in them.

Vaibhav
+4  A: 

<off-topic-rant>

I loathe sealed classes. Even if the performance benefits are astounding (which I doubt), they destroy the object-oriented model by preventing reuse via inheritance. For example, the Thread class is sealed. While I can see that one might want threads to be as efficient as possible, I can also imagine scenarios where being able to subclass Thread would have great benefits. Class authors, if you must seal your classes for "performance" reasons, please provide an interface at the very least so we don't have to wrap-and-replace everywhere that we need a feature you forgot.

Example: SafeThread had to wrap the Thread class because Thread is sealed and there is no IThread interface; SafeThread automatically traps unhandled exceptions on threads, something completely missing from the Thread class. [and no, the unhandled exception events do not pick up unhandled exceptions in secondary threads].

</off-topic-rant>

Steven A. Lowe
I don't seal my classes for performance reasons. I seal them for design reasons. Designing for inheritance is hard, and that effort will be wasted most of the time. I totally agree about providing interfaces though - that's a *far* superior solution to unsealing classes.
Jon Skeet
@[Jon Skeet]: do you write classes for libraries that other developers will be using? I don't think that desigining for inheritance is hard, what am I missing? Also, interfaces are great, but they're no substitute for inheritance, they're just better than nothing.
Steven A. Lowe
Encapsulation is generally a better solution than inheritance. To take your specific thread example, trapping thread exceptions breaks the Liskov Substitution Principle because you've changed the documented behaviour of the Thread class, so even if you could derive from it, it would not be reasonable to say that you could use SafeThread everywhere you could use Thread. In this case, you would be better to encapsulate Thread into another class which has different documented behaviour, which you are able to do. Sometimes things are sealed for your own good.
Greg Beech
@[Greg Beech]: opinion, not fact - being able to inherit from Thread to fix a heinous oversight in its design is NOT a bad thing ;-) And I think you're overstating LSP - the provabable property q(x) in this case is 'an unhandled exception destroys the program' which is not a "desirable property" :-)
Steven A. Lowe
-1 for not understanding that too much of code flexibility usually only leads to abuse.
Turing Complete
@Turing interesting set of assumptions; I'm guessing that you drive a slot-car ;-)
Steven A. Lowe
No, but I have had my share of crappy code where I enabled other developers to abuse my stuff by not sealing it or by allowing corner cases.Most of my code nowadays is assertions and other contract related stuff. And I'm quite open about the fact that I do this only to be a pain in the ***.
Turing Complete
+24  A: 

The answer is no, sealed classes do not perform better than non-sealed.

The issue comes down to the call vs callvirt IL op codes. Call is faster than callvirt, and callvirt is mainly used when you don't know if the object has been subclassed. So people assume that if you seal a class all the op codes will change from calvirts to calls and will be faster.

Unfortunately callvirt does other things that make it useful too, like checking for null references. This means that even if a class is sealed, the reference might still be null and thus a callvirt is needed. You can get around this (without needing to seal the class), but it becomes a bit pointless.

Structs use call because they cannot be subclassed and are never null.

See this question for more information:

Call and callvirt

Cameron MacFarland
This should be the accepted answer.
mafutrct
+1  A: 

Marking a class sealed should have no performance impact.

There are cases where csc might have to emit a callvirt opcode instead of a call opcode. However, it seems those cases are rare.

And it seems to me that the JIT should be able to emit the same non-virtual function call for callvirt that it would for call, if it knows that the class doesn't have any subclasses (yet). If only one implementation of the method exists, there's no point loading its address from a vtable—just call the one implementation directly. For that matter, the JIT can even inline the function.

It's a bit of a gamble on the JIT's part, because if a subclass is later loaded, the JIT will have to throw away that machine code and compile the code again, emitting a real virtual call. My guess is this doesn't happen often in practice.

(And yes, VM designers really do aggressively pursue these tiny performance wins.)

Jason Orendorff
A: 

Run this code and you'll see that sealed classes are 2 times faster:

class Program
{
    static void Main(string[] args)
    {
        Console.ReadLine();

        var watch = new Stopwatch();
        watch.Start();
        for (int i = 0; i < 10000000; i++)
        {
            new SealedClass().GetName();
        }
        watch.Stop();
        Console.WriteLine("Sealed class : {0}", watch.Elapsed.ToString());

        watch.Start();
        for (int i = 0; i < 10000000; i++)
        {
            new NonSealedClass().GetName();
        }
        watch.Stop();
        Console.WriteLine("NonSealed class : {0}", watch.Elapsed.ToString());

        Console.ReadKey();
    }
}

sealed class SealedClass
{
    public string GetName()
    {
        return "SealedClass";
    }
}

class NonSealedClass
{
    public string GetName()
    {
        return "NonSealedClass";
    }
}

output: Sealed class : 00:00:00.1897568 NonSealed class : 00:00:00.3826678

RuslanG
There's a couple of problems with this. First off, you're not resetting the stopwatch between the first and second test. Second, the way you're calling the method means that all op codes will be call not callvirt so the type doesn't matter.
Cameron MacFarland
A: 

RuslanG,

You forgot to call watch.Reset() after running first test.

o_O

:]

Nerijus Narmontas
A: 

I consider "sealed" classes the normal case and I ALWAYS have a reason to omit the "sealed" keyword.

The most important reasons for me are:

a) Better compile time checks (casting to interfaces not implemented will be detected at compile time, not only at runtime)

and, top reason:

b) Abuse of my classes is not possible that way

I wish Microsoft would have made "sealed" the standard, not "unsealed".

Turing Complete