Haben versiegelte Klassen wirklich Leistungsvorteile bieten?

stimmen
118

Ich habe über viele Tipps zur Optimierung kommen, die sagen, dass Sie Ihre Klassen markieren sollten versiegelt, um zusätzliche Leistungsvorteile zu erhalten.

Ich lief einige Tests die Performance Differential zu überprüfen und fand keine. Mache ich etwas falsch? Fehle ich den Fall, in dem versiegelten Klassen bessere Ergebnisse geben werden?

Hat jemand laufen Tests und einen Unterschied gesehen?

Helfen Sie mir lernen :)

Veröffentlicht am 05/08/2008 um 13:00
quelle vom benutzer
In anderen Sprachen...                            


11 antworten

stimmen
133

Die Antwort ist nein, versiegelte Klassen führen nicht besser als nicht-versiegelt.

Das Problem kommt auf die callvs callvirtIL op - Codes. Callist schneller als callvirt, und in callvirterster Linie verwendet, wenn Sie nicht wissen , ob das Objekt subclassed wurde. So können die Leute davon ausgehen , dass , wenn Sie eine Klasse versiegeln alle op - Codes von ändern calvirtszu callsund wird schneller sein.

Leider callvirttut andere Dinge , die es nützlich zu machen, wie für null Referenzen zu prüfen. Dies bedeutet , dass selbst wenn eine Klasse abgedichtet ist, nach wie vor die Referenz Null sein könnte und somit ein callvirtbenötigt. Sie können dieses Problem umgehen , (ohne dass die Klasse zu versiegeln), aber es wird ein bisschen sinnlos.

Structs verwenden , callweil sie nicht unterklassiert werden können und nie null.

Sehen Sie diese Frage für weitere Informationen:

Anrufen und callvirt

Beantwortet am 24/12/2008 um 03:09
quelle vom benutzer

stimmen
46

Der Jitter wird manchmal verwendet nicht-virtuelle Anrufe in versiegelten Klassen Methoden, da es keine Möglichkeit gibt, können sie weiter ausgebaut werden.

Es gibt komplexe Regeln in Bezug auf Berufung Typ, virtuelle / nicht virtuell, und ich kenne sie nicht alle, so kann ich wirklich nicht, sie für Sie umreißen, aber wenn Sie für versiegelte Klassen und virtuelle Methoden Google können Sie einige Artikel zum Thema finden.

Beachten Sie, dass jede Art von Performance-Vorteil, den Sie von dieser Ebene der Optimierung erhalten würde sollte als letzter Ausweg angesehen werden, immer optimieren auf der algorithmischen Ebene, bevor Sie den Code-Ebene zu optimieren.

Hier ist ein Link zu diesem erwähnen: Rambling auf dem versiegelten Schlüsselwort

Beantwortet am 05/08/2008 um 13:32
quelle vom benutzer

stimmen
20

Wie ich weiß, gibt es keine Garantie für die Performance - Vorteil. Aber es gibt eine Chance , Leistungseinbuße unter einem bestimmten Zustand zu verringern , mit versiegelten Verfahren. (sealed Klasse macht alle Methoden abgedichtet werden.)

Aber es ist bis zu Compiler Implementierung und Ausführungsumgebung.


Einzelheiten

Viele der modernen CPUs verwenden lange Pipeline-Struktur Leistung zu erhöhen. Da CPU unglaublich schneller als Speicher vorhanden ist, hat die CPU Code aus dem Speicher Prefetch-Pipeline zu beschleunigen. Wenn der Code nicht bereit, auf die richtige Zeit ist, werden die Pipelines im Leerlauf sein.

Es gibt ein großes Hindernis genannt dynamische Dispatch , die diese ‚Prefetching‘ Optimierung stört. Sie können dies nur als eine bedingte Verzweigung verstehen.

// Value of `v` is unknown,
// and can be resolved only at runtime.
// CPU cannot know code to prefetch,
// so just prefetch one of a() or b().
// This is *speculative execution*.
int v = random();
if (v==1) a();
else b();

CPU kann nächste Code in diesem Fall ausgeführt werden, da die nächste Codeposition unbekannt ist nicht vorab abzurufen , bis der Zustand behoben ist. So macht diese Gefahr Pipeline im Leerlauf verursacht. Und Leistungseinbuße durch Leerlauf ist in regelmäßigen riesig.

Ähnliches, was bei Verfahren überwiegenden passieren. Compiler kann überwiegende richtige Methode für aktuellen Methodenaufruf bestimmen, aber manchmal ist es unmöglich. In diesem Fall kann eine geeignete Methode nur zur Laufzeit bestimmt werden. Dies ist auch ein Fall von dynamischer Dispatch und ein Hauptgrund von dynamisch typisierten Sprachen sind in der Regel langsamer als statisch typisierten Sprachen.

Einige CPU (einschließlich der jüngsten Intel-x86 - Chips) verwendet Technik namens spekulative Ausführung Pipeline auch auf die Situation zu nutzen. Prefetch nur eine der Ausführungspfad. Aber Trefferrate dieser Technik ist nicht so hoch. Und Spekulation Versagen verursacht Pipeline - Blockierung , die auch große Leistungseinbuße macht. (dies ist vollständig von CPU - Implementierung. Einige mobilen CPU bekannt sind , wie nicht diese Art von Optimierung Energie zu sparen)

Grundsätzlich ist C # eine statisch kompilierte Sprache. Aber nicht immer. Ich weiß nicht genau Zustand und das ist völlig bis zu Compiler - Implementierung. Einige Compiler kann Möglichkeit der dynamischen Dispatch eliminieren , indem verhindert Verfahren überwiegendes , wenn das Verfahren als markiert sealed. Dumm Compiler möglicherweise nicht. Dies ist der Leistungsvorteil der sealed.


Diese Antwort ( Warum ist es schneller eine sortierte Array als eine unsortierte Array zu verarbeiten? ) Beschreibt die Verzweigungsvorhersage viel besser.

Beantwortet am 03/02/2011 um 16:22
quelle vom benutzer

stimmen
18

Update: Wie von .NET Core 2.0 und .NET-Desktop 4.7.1, jetzt die CLR devirtualization unterstützt. Es kann in geschlossenen Klassen Methoden nehmen und virtuelle Anrufe mit direktem Anruf ersetzen - und es kann dies auch für nicht-versiegelten Klassen tun, wenn sie herausfinden, es ist sicher, dies zu tun.

In einem solchen Fall (eine versiegelte Klasse, die die CLR nicht anders als sicher devirtualise erkennen konnte), eine versiegelte Klasse sollte eigentlich eine Art von Leistungsvorteil bieten.

Das heißt, würde ich es nicht für sich Gedanken wert sein würde , es sei denn Sie hatte bereits den Code profiliert und festgestellt , dass Sie in einem besonders heißen Pfad waren Millionen Mal aufgerufen wird, oder so ähnlich:

https://blogs.msdn.microsoft.com/dotnet/2017/06/29/performance-improvements-in-ryujit-in-net-core-and-net-framework/


Original Antwort:

Ich habe das folgende Testprogramm, und dann dekompiliert es Reflektor zu sehen, was Code MSIL wurde emittiert.

public class NormalClass {
    public void WriteIt(string x) {
        Console.WriteLine("NormalClass");
        Console.WriteLine(x);
    }
}

public sealed class SealedClass {
    public void WriteIt(string x) {
        Console.WriteLine("SealedClass");
        Console.WriteLine(x);
    }
}

public static void CallNormal() {
    var n = new NormalClass();
    n.WriteIt("a string");
}

public static void CallSealed() {
    var n = new SealedClass();
    n.WriteIt("a string");
}

In allen Fällen gibt die C # Compiler (Visual Studio 2010 Release Build-Konfiguration) identisch MSIL, die wie folgt lautet:

L_0000: newobj instance void <NormalClass or SealedClass>::.ctor()
L_0005: stloc.0 
L_0006: ldloc.0 
L_0007: ldstr "a string"
L_000c: callvirt instance void <NormalClass or SealedClass>::WriteIt(string)
L_0011: ret 

Die oft zitierte Grund , dass die Menschen versiegelt sagen Leistungsvorteile bietet , ist , dass der Compiler kennt die Klasse nicht außer Kraft gesetzt wird, und kann somit verwenden callstatt , callvirtda es nicht für virtuals zu prüfen hat, usw. Wie oben bewiesen, ist dies nicht wahr.

Mein nächster Gedanke war, dass, obwohl die MSIL identisch ist, vielleicht die JIT-Compiler unterschiedlich behandelt Klassen versiegelt?

Ich lief ein Release-Build unter den Visual Studio-Debugger und betrachten den dekompilierten x86-Ausgang. In beiden Fällen war der x86-Code mit Ausnahme der Klassennamen und Funktionsspeicheradressen identisch sind, (die natürlich unterschiedlich sein müssen). Hier ist es

//            var n = new NormalClass();
00000000  push        ebp 
00000001  mov         ebp,esp 
00000003  sub         esp,8 
00000006  cmp         dword ptr ds:[00585314h],0 
0000000d  je          00000014 
0000000f  call        70032C33 
00000014  xor         edx,edx 
00000016  mov         dword ptr [ebp-4],edx 
00000019  mov         ecx,588230h 
0000001e  call        FFEEEBC0 
00000023  mov         dword ptr [ebp-8],eax 
00000026  mov         ecx,dword ptr [ebp-8] 
00000029  call        dword ptr ds:[00588260h] 
0000002f  mov         eax,dword ptr [ebp-8] 
00000032  mov         dword ptr [ebp-4],eax 
//            n.WriteIt("a string");
00000035  mov         edx,dword ptr ds:[033220DCh] 
0000003b  mov         ecx,dword ptr [ebp-4] 
0000003e  cmp         dword ptr [ecx],ecx 
00000040  call        dword ptr ds:[0058827Ch] 
//        }
00000046  nop 
00000047  mov         esp,ebp 
00000049  pop         ebp 
0000004a  ret 

Ich dachte dann vielleicht unter dem Debugger ausgeführt wird bewirkt, dass es weniger aggressive Optimierung durchzuführen?

Ich lief dann ein Standalone-Release ausführbare außerhalb jeder Debug-Umgebung aufzubauen, und verwendet WinDBG + SOS zu brechen, nachdem das Programm beendet hatte, und sehen Sie die dissasembly des JIT-x86-Code kompiliert.

Wie Sie unten aus dem Code sehen können, wenn sie außerhalb des Debuggers des JIT - Compiler ausgeführt ist aggressiver, und es hat die inlined WriteItMethode direkt in die Anrufer. Entscheidend ist jedoch, dass es identisch war , als eine abgedichtete vs nicht-versiegelten Klasse aufrufen. Es gibt keinen Unterschied überhaupt zwischen einer versiegelten oder nonsealed Klasse.

Hier ist es, wenn eine normale Klasse aufrufen:

Normal JIT generated code
Begin 003c00b0, size 39
003c00b0 55              push    ebp
003c00b1 8bec            mov     ebp,esp
003c00b3 b994391800      mov     ecx,183994h (MT: ScratchConsoleApplicationFX4.NormalClass)
003c00b8 e8631fdbff      call    00172020 (JitHelp: CORINFO_HELP_NEWSFAST)
003c00bd e80e70106f      call    mscorlib_ni+0x2570d0 (6f4c70d0) (System.Console.get_Out(), mdToken: 060008fd)
003c00c2 8bc8            mov     ecx,eax
003c00c4 8b1530203003    mov     edx,dword ptr ds:[3302030h] ("NormalClass")
003c00ca 8b01            mov     eax,dword ptr [ecx]
003c00cc 8b403c          mov     eax,dword ptr [eax+3Ch]
003c00cf ff5010          call    dword ptr [eax+10h]
003c00d2 e8f96f106f      call    mscorlib_ni+0x2570d0 (6f4c70d0) (System.Console.get_Out(), mdToken: 060008fd)
003c00d7 8bc8            mov     ecx,eax
003c00d9 8b1534203003    mov     edx,dword ptr ds:[3302034h] ("a string")
003c00df 8b01            mov     eax,dword ptr [ecx]
003c00e1 8b403c          mov     eax,dword ptr [eax+3Ch]
003c00e4 ff5010          call    dword ptr [eax+10h]
003c00e7 5d              pop     ebp
003c00e8 c3              ret

Vs einer versiegelten Klasse:

Normal JIT generated code
Begin 003c0100, size 39
003c0100 55              push    ebp
003c0101 8bec            mov     ebp,esp
003c0103 b90c3a1800      mov     ecx,183A0Ch (MT: ScratchConsoleApplicationFX4.SealedClass)
003c0108 e8131fdbff      call    00172020 (JitHelp: CORINFO_HELP_NEWSFAST)
003c010d e8be6f106f      call    mscorlib_ni+0x2570d0 (6f4c70d0) (System.Console.get_Out(), mdToken: 060008fd)
003c0112 8bc8            mov     ecx,eax
003c0114 8b1538203003    mov     edx,dword ptr ds:[3302038h] ("SealedClass")
003c011a 8b01            mov     eax,dword ptr [ecx]
003c011c 8b403c          mov     eax,dword ptr [eax+3Ch]
003c011f ff5010          call    dword ptr [eax+10h]
003c0122 e8a96f106f      call    mscorlib_ni+0x2570d0 (6f4c70d0) (System.Console.get_Out(), mdToken: 060008fd)
003c0127 8bc8            mov     ecx,eax
003c0129 8b1534203003    mov     edx,dword ptr ds:[3302034h] ("a string")
003c012f 8b01            mov     eax,dword ptr [ecx]
003c0131 8b403c          mov     eax,dword ptr [eax+3Ch]
003c0134 ff5010          call    dword ptr [eax+10h]
003c0137 5d              pop     ebp
003c0138 c3              ret

Für mich stellt dieser festen Beweis , dass es nicht kann jede Leistungsverbesserung zwischen Aufrufen von Methoden auf vs nicht-versiegelten Klassen versiegelt sein ... Ich glaube , ich bin jetzt glücklich :-)

Beantwortet am 20/02/2012 um 21:45
quelle vom benutzer

stimmen
4

Eine Klasse - Kennzeichnung sealedsollte keine Auswirkungen auf die Leistung haben.

Es gibt Fälle , in denen cscein emittieren könnte müssen callvirtOpcode statt eines callOpcode. Allerdings scheint es diese Fälle sind selten.

Und es scheint mir , dass die JIT Lage sein sollte , den gleichen nicht-virtuellen Funktionsaufruf zu emittieren , für callvirtdass es würde für call, wenn sie weiß , dass die Klasse keine Subklassen hat (noch) nicht . Wenn nur eine Implementierung des Verfahrens vorhanden ist , gibt es keinen Punkt Laden seine Adresse aus einer VTable-nur direkt die einer Implementierung nennen. Was das betrifft, kann der JIT - Inline auch die Funktion.

Es ist ein bisschen wie ein Glücksspiel auf dem Teil des JIT, denn wenn eine Unterklasse wird später geladen, wird die JIT wegzuwerfen Code , dass die Maschine muß und den Code erneut kompiliert, eine echte virtuelle Call emittieren. Meine Vermutung ist , das passiert nicht oft in der Praxis.

(Und ja, VM-Designer wirklich verfolgen aggressiv diese winzige Leistung gewinnt.)

Beantwortet am 20/11/2009 um 10:53
quelle vom benutzer

stimmen
3

I „versiegelt“ Klassen den Normalfall betrachten, und ich habe immer einen Grund, die „versiegelt“ Schlüsselwort wegzulassen.

Die wichtigsten Gründe für mich sind:

a) Bessere Zeitkontrollen kompilieren (Gießen Schnittstellen werden bei der Kompilierung nicht umgesetzt werden erfasst, nicht nur zur Laufzeit)

und Top-Grund:

b) Missbrauch meiner Klassen ist nicht möglich, auf diese Weise

Ich wünschte, Microsoft würde „versiegelt“ gemacht hat den Standard, nicht „unverschlossenen“.

Beantwortet am 03/08/2010 um 15:27
quelle vom benutzer

stimmen
3

<Off-Topic-rant>

Ich verabscheue versiegelte Klassen. Auch wenn die Performance - Vorteile sind erstaunlich (was ich bezweifle), sie zerstören das objektorientierte Modell durch Wiederverwendung durch Vererbung zu verhindern. Zum Beispiel wird die Thread - Klasse versiegelt. Während ich sehen kann , dass ein Gewinde so effizient wie möglich sein könnte , will, kann ich auch Szenarien vorstellen , wo Themas in der Lage, eine Unterklasse große Vorteile haben würde. Klasse Autoren, wenn Sie müssen Ihre Klassen für „Performance“ Gründe abdichten, bitte eine Schnittstelle bereitstellen , zumindest so müssen wir nicht überall wickeln und Ersetzen , die wir brauchen eine Funktion , die Sie vergessen haben.

Beispiel: SafeThread hatte die Klasse Thread zu wickeln , weil Gewinde verschlossen ist und es gibt keine IThread - Schnittstelle; SafeThread Fallen automatisch nicht behandelte Ausnahmen auf Threads, etwas völlig aus der Thread - Klasse fehlt. [und nein, die nicht behandelte Ausnahme - Event nicht abholen nicht behandelte Ausnahmen in sekundären Threads].

</ Off-Topic-rant>

Beantwortet am 14/10/2008 um 21:04
quelle vom benutzer

stimmen
3

Versiegelte Klassen sollten eine Leistungsverbesserung bieten. Da eine versiegelte Klasse kann nicht abgeleitet werden kann, können beliebige virtuelle Elemente in nicht virtuellen Elemente gedreht werden.

Natürlich sprechen wir wirklich kleine Gewinne. Ich würde nicht eine Klasse markieren, wie versiegelt nur eine Leistungsverbesserung zu erhalten, es sei denn Profilierung enthüllt ein Problem zu sein.

Beantwortet am 05/08/2008 um 13:37
quelle vom benutzer

stimmen
2

versiegelte Klassen mindestens ein klein wenig schneller sein, aber manchmal kann waayyy schneller sein ... wenn der JIT-Optimizer Anrufe Inline können, die sonst gewesen virtuelle Anrufe haben würde. Also, wo es oft genannten Methoden, die klein genug sind, werden inlined, sollten Sie auf jeden Fall die Klasse abdichten.

Allerdings ist der beste Grund, um eine Klasse zu versiegeln ist zu sagen: „Ich habe nicht diesen Entwurf von vererbt werden, so werde ich nicht zulassen, Sie davon aus, es wurde verbrannt so ausgestaltet sein, und ich werde nicht zu verbrennen, indem ich in eine Implementierung immer gesperrt, weil ich dich daraus ableiten lassen.“

Ich weiß, haben einige hier sagten, dass sie versiegelte Klassen hassen, weil sie die Möglichkeit, wollen von etwas ableiten ... aber das ist oft nicht die wartbar Wahl ... weil eine Klasse Belichtung in vielen Schleusen Sie Ableitungs mehr als nicht alle Belichtungs Das. Sein ähnliches sagen: „Ich Klassen verabscheuen, die privaten Mitglieder haben ... Ich kann oft nicht die Klasse machen, was ich will, weil ich keinen Zugang habe.“ Die Einkapselung ist wichtig, ... Abdichtung ist eine Form der Verkapselung.

Beantwortet am 01/10/2011 um 11:37
quelle vom benutzer

stimmen
2

@Vaibhav, welche Art von Tests haben führen Sie die Leistung zu messen?

Ich denke , man müßte verwenden Rotor und in CLI zu bohren und verstehen , wie eine versiegelte Klasse Leistung verbessern würde.

SSCLI (Rotor)
SSCLI: Sscli

Die Common Language Infrastructure (CLI) ist der ECMA-Standard, der den Kern des .NET Framework beschreibt. Die Shared Source CLI (SSCLI), auch als Rotor genannt, ist ein komprimiertes Archiv des Quellcodes an eine funktionierende Implementierung der ECMA CLI und die ECMA C # Sprachspezifikation, Technologien im Mittelpunkt der Microsoft .NET-Architektur.

Beantwortet am 05/08/2008 um 13:40
quelle vom benutzer

stimmen
-9

Führen Sie diesen Code und Sie werden sehen, dass versiegelte Klassen sind 2-mal schneller:

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";
    }
}

Ausgang: Versiegelte Klasse: 00: 00: 00,1897568 NonSealed Klasse: 00: 00: 00,3826678

Beantwortet am 26/11/2009 um 18:50
quelle vom benutzer

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more