Est-ce que les classes scellées offrent vraiment la performance avantages?

voix
118

Je suis venu à travers beaucoup de conseils d'optimisation qui disent que vous devez marquer vos classes comme scellé pour obtenir des prestations de performance supplémentaires.

J'ai couru quelques tests pour vérifier l'écart de performance et a trouvé aucun. Est-ce que je fais quelque chose de mal? Est-ce que je manque le cas où les cours scellés donneront de meilleurs résultats?

Est-ce que d'exécuter des tests et tout le monde vu une différence?

Aidez-moi à apprendre :)

Créé 05/08/2008 à 13:00
source utilisateur
Dans d'autres langues...                            


11 réponses

voix
133

La réponse est non, les classes scellées ne fonctionnent pas mieux que les non-scellé.

La question se résume à callvs callvirtcodes IL op. Callest plus rapide que callvirt, et callvirtest principalement utilisé lorsque vous ne savez pas si l'objet a été sous - classé. Donc , les gens supposent que si vous scellez une classe tous les codes op passera de calvirtsà callset sera plus rapide.

Malheureusement , le callvirtfait d' autres choses qui le rendent utile aussi, comme la vérification des références null. Cela signifie que même si une classe est fermée, la référence pourrait encore être nulle et donc un callvirtbesoin. Vous pouvez contourner ce (sans avoir besoin de sceller la classe), mais il devient un peu inutile.

Struct utilisent callparce qu'ils ne peuvent pas être sous - et ne sont jamais nuls.

Voir cette question pour plus d'informations:

Appel et callvirt

Créé 24/12/2008 à 03:09
source utilisateur

voix
46

Le JITter utilisent parfois des appels non virtuels à des méthodes dans les classes scellées car il n'y a aucun moyen de pouvoir être étendues.

Il existe des règles complexes concernant le type d'appel, virtuel / non virtuels, et je ne les connais pas tous, donc je ne peux pas vraiment les décrire pour vous, mais si vous google pour les classes scellées et des méthodes virtuelles que vous pouvez trouver des articles sur le sujet.

Notez que doit être considérée tout type de prestation de performance vous obtenir de ce niveau d'optimisation en dernier recours, d'optimiser toujours au niveau algorithmique avant d'optimiser le niveau du code.

Voici un lien mentionnant ceci: Rambling sur le mot - clé scellé

Créé 05/08/2008 à 13:32
source utilisateur

voix
20

Comme je sais, il n'y a aucune garantie de bénéfice de la performance. Mais il y a une chance de réduire la peine de performance sous une condition spécifique avec la méthode scellée. (classe scellée fait toutes les méthodes à étancher.)

Mais il est à la mise en œuvre du compilateur et environnement d'exécution.


Détails

Un grand nombre d'unités centrales modernes utilisent la structure de pipeline longue pour augmenter les performances. Parce que CPU est incroyablement plus rapide que la mémoire, le processeur doit précharger code de la mémoire pour accélérer pipeline. Si le code est prêt au bon moment, les pipelines seront au repos.

Il y a un grand obstacle appelé dispatch dynamique qui perturbe cette optimisation « prélecture ». Vous pouvez comprendre cela comme un simple branchement conditionnel.

// 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 ne peut précharger le code suivant pour exécuter dans ce cas parce que la position suivante de code est inconnu jusqu'à ce que la condition soit résolue. Donc , ce qui rend danger provoque ralenti du pipeline. Et pénalité de performance en ralenti est énorme dans régulière.

chose semblable se produit en cas de passer outre la méthode. Compilateur peut déterminer la méthode appropriée primordiale pour l'appel de la méthode actuelle, mais il est parfois impossible. Dans ce cas, la méthode appropriée peut être déterminée que lors de l'exécution. Ceci est également un cas d'envoi dynamique, et, une raison principale des langues dynamiquement typés sont généralement plus lents que les langues statiquement typé.

Certains CPU (y compris les puces x86 de Intel récent) utilise la technique appelée exécution spéculative à utiliser même pipeline sur la situation. Il suffit de précharger l' un des chemin d'exécution. Mais le taux de succès de cette technique est pas si élevé. Et l' échec de la spéculation provoque décrochage de pipeline qui fait également pénalité énorme performance. (ce qui est tout à fait par la mise en œuvre du processeur. une CPU mobile est connu comme le fait pas ce genre d'optimisation pour économiser l' énergie)

Fondamentalement, C # est un langage compilé statiquement. Mais pas toujours. Je ne sais pas état exact et ce qui est entièrement à la mise en œuvre du compilateur. Certains compilateurs peuvent éliminer la possibilité d'envoi dynamique en empêchant le remplacement d' un méthode si la méthode est marquée comme sealed. Les compilateurs stupides ne peuvent pas. C'est l'avantage de la performance du sealed.


Cette réponse ( Pourquoi est - il plus rapide de traiter un tableau trié qu'un tableau non trié? ) Décrit la prédiction de branchement beaucoup mieux.

Créé 03/02/2011 à 16:22
source utilisateur

voix
18

Mise à jour: A partir de .NET Core 2.0 et .NET 4.7.1 de bureau, le CLR prend désormais en charge devirtualization. Il peut prendre des méthodes dans les classes scellées et remplacer les appels virtuels avec des appels directs - et il peut aussi le faire pour les classes non scellées si elle peut comprendre qu'il est sûr de le faire.

Dans un tel cas (une classe scellée que le CLR n'a pas pu détecter autrement comme sûr à devirtualise), une classe scellée devrait offrir en fait une sorte de bénéfice de la performance.

Cela dit, je ne pense pas que ce serait la peine se soucier de moins que vous aviez déjà profilé le code et déterminé que vous étiez dans un chemin particulièrement chaud étant appelé des millions de fois, ou quelque chose comme ça:

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


Réponse d'origine:

J'ai fait le programme de test suivant, puis décompilé à l'aide de réflecteur pour voir quel code MSIL a été émis.

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

Dans tous les cas, le compilateur C # (Visual Studio 2010 dans la configuration de version Release) émet MSIL identique, ce qui est la suivante:

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 

La raison souvent citée que les gens disent scellés offre des avantages de performance est que le compilateur connaît la classe n'est pas surchargée, et peut donc utiliser au calllieu de callvirtcar il ne doit pas vérifier virtuals, etc. Comme démontré ci - dessus, ce n'est pas vrai.

Ma pensée suivante était que même si le MSIL est identique, peut-être le compilateur JIT traite les classes scellées différemment?

J'ai couru une version release sous le débogueur de Visual Studio et ont visionné la sortie x86 décompilé. Dans les deux cas, le code x86 était identique, à l'exception des noms de classe et les adresses mémoire de fonction (ce qui bien sûr doit être différent). C'est ici

//            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 

Je pense alors peut-être en cours d'exécution sous le débogueur l'amène à effectuer une optimisation moins agressive?

J'ai alors couru une version autonome à construire en dehors de tout environnement exécutable de débogage, et utilisé WinDBG + SOS pour briser après le programme avait terminé, et consulter la dissasembly du JIT compilé code x86.

Comme vous pouvez le voir dans le code ci - dessous, lors de l' exécution en dehors du débogueur le compilateur JIT est plus agressif, et il a inline la WriteItméthode linéaire dans l'appelant. L'essentiel est cependant que c'était identique lors de l' appel d' un scellé vs classe non étanche. Il n'y a aucune différence entre une classe scellée ou nonsealed.

Ici, il est quand appeler une classe normale:

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 une classe scellée:

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

Pour moi, cela fournit une preuve solide qu'il ne peut y avoir de l' amélioration de la performance entre l' appel de méthodes sur scellées vs classes non scellées ... Je pense que je suis heureux maintenant :-)

Créé 20/02/2012 à 21:45
source utilisateur

voix
4

Marquage d' une classe sealeddevrait avoir aucun impact sur les performances.

Il y a des cas où cscpourraient avoir à émettre un callvirtopcode au lieu d'un callopcode. Cependant, il semble que ces cas sont rares.

Et il me semble que le JIT devrait être en mesure d'émettre le même appel de fonction non virtuelle pour callvirtque ce serait pour call, si elle sait que la classe n'a pas de sous - classes (encore). Si une seule mise en œuvre de la méthode existe, il n'y a pas de point de chargement de son adresse d'un vtable- il suffit d' appeler une mise en œuvre directement. Pour cette question, le JIT peut même inline la fonction.

Il est un peu un pari de la part du JIT, parce que si une sous - classe est plus tard chargé, le JIT devra jeter ce code machine et recompiler le code, l' émission d' un réel appel virtuel. Je suppose que cela ne se produit pas souvent dans la pratique.

(Et oui, les designers VM ne vraiment poursuivre agressivement ces petites victoires de performance.)

Créé 20/11/2009 à 10:53
source utilisateur

voix
3

Je considère que les classes « scellés » le cas normal et j'ai toujours une raison d'omettre le mot-clé « scellé ».

Les raisons les plus importantes pour moi sont:

a) Une meilleure compilation des contrôles de temps (coulée aux interfaces mises en œuvre ne sera détectée au moment de la compilation, non seulement lors de l'exécution)

et principale raison:

b) L'abus de mes cours n'est pas possible de cette façon

Je souhaite que Microsoft aurait fait « scellé » la norme, pas « non scellée ».

Créé 03/08/2010 à 15:27
source utilisateur

voix
3

<-Diatribe hors-sujet>

Je déteste les classes scellées. Même si les avantages de performance sont étonnants ( ce dont je doute), ils détruisent le modèle orienté objet en empêchant la réutilisation par héritage. Par exemple, la classe Thread est scellé. Alors que je peux voir que l' on pourrait désirer FILETAGES aussi efficace que possible, je peux aussi imaginer des scénarios où l' être en mesure de sous - classe de cette discussion aurait de grands avantages. Les auteurs de la classe, si vous devez sceller vos classes pour des raisons de « performance », s'il vous plaît fournir une interface à tout le moins que nous ne devons pas envelopper-et remplaçons partout que nous avons besoin d' une fonction vous avez oublié.

Exemple: SafeThread a dû envelopper la classe de fil parce que cette discussion est scellé et il n'y a pas d' interface IThread; SafeThread emprisonne automatiquement les exceptions non gérées sur des fils, quelque chose de complètement absent de la classe Thread. [et non, les événements d'exception non gérée ne pas ramasser les exceptions non gérées dans les discussions secondaires].

</ Hors-sujet-diatribe>

Créé 14/10/2008 à 21:04
source utilisateur

voix
3

Les classes Sealed devraient fournir une amélioration de la performance. Puisque ne peut être dérivé une classe scellée, les membres virtuels peuvent être transformés en éléments non virtuels.

Bien sûr, nous parlons des gains vraiment petits. Je ne voudrais pas marquer une classe comme scellée juste pour obtenir une amélioration de la performance à moins que le profilage a révélé être un problème.

Créé 05/08/2008 à 13:37
source utilisateur

voix
2

les classes cachetées seront au moins un petit peu plus rapide, mais peut parfois être waayyy plus vite ... si l'optimiseur JIT peut inline appels qui auraient autrement été appels virtuels. Donc, là où il y a des méthodes souvent appelé qui sont assez petites pour être inline, certainement envisager la classe d'étanchéité.

Cependant, la meilleure raison pour sceller une classe est de dire: « Je ne l'ai pas concevoir que cela soit héritée de, donc je ne vais pas vous laisser brûler en supposant qu'il a été conçu pour être, et je ne vais pas de me brûler en s'enfermer dans une mise en œuvre parce que je vous laisse en tirer « .

Je sais que certains ici ont dit qu'ils détestent les classes fermées parce qu'ils veulent avoir la possibilité de tirer de quoi que ce soit ... mais ce qui est rarement le choix le plus maintenable ... parce que l'exposition d'une classe à dérivation vous enferme dans beaucoup plus de ne pas exposer tous cette. Son semblable à dire: « Je déteste les classes qui ont des députés ... Je ne peux pas souvent faire la classe ce que je veux parce que je n'ai pas accès. » Encapsulation est important ... étanchéité est une forme d'encapsulation.

Créé 01/10/2011 à 11:37
source utilisateur

voix
2

@Vaibhav, quel genre de tests avez-vous exécuter pour mesurer la performance?

Je suppose que l' on aurait à utiliser du rotor et de percer dans CLI et de comprendre comment une classe fermée améliorerait les performances.

SSCLI (rotor)
SSCLI: Shared Source Infrastructure Common Language

Le Common Language Infrastructure (CLI) est la norme ECMA qui décrit le noyau du .NET Framework. Le Shared Source CLI (SSCLI), également connu sous le nom du rotor, est une archive compressée du code source pour une implémentation fonctionnelle de la CLI ECMA et l'ECMA C # spécification du langage, les technologies au cœur de l'architecture .NET de Microsoft.

Créé 05/08/2008 à 13:40
source utilisateur

voix
-9

Exécutez ce code et vous verrez que les classes sont fermées 2 fois plus vite:

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

Sortie: classe sealed: 00: 00: 00,1897568 classe NonSealed: 00: 00: 00,3826678

Créé 26/11/2009 à 18:50
source utilisateur

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