Vra

Ek het kom oor'n baie van die optimalisering wenke wat sê dat jy moet merk jou klasse as verseël te kry ekstra prestasie voordele.

Ek het'n paar toetse om seker te maak die prestasie ewenaar en het niemand gevind nie.Doen ek iets verkeerd?Ek mis die geval waar verseël klasse sal gee beter resultate?

Het iemand hardloop toetse en gesien hoe'n verskil?

Help my leer :)

Was dit nuttig?

Oplossing

Die beweging sal soms gebruik nie-virtuele oproepe na metodes in verseëlde klasse want daar is geen manier waarop hulle kan verder uitgebrei word.

Daar is komplekse reëls met betrekking tot tipe roeping, virtuele / nonvirtual, en ek ken hulle nie almal so ek kan nie regtig hulle uiteen te sit vir jou, maar as jy Google vir verseël klasse en virtuele metodes wat jy kan 'n paar artikels oor vind die onderwerp.

Let daarop dat enige vorm van prestasie voordeel wat u sal kry uit hierdie vlak van optimalisering as laaste uitweg beskou moet word, altyd optimaliseer op die algoritmiese vlak voor jy optimaliseer op die kode-vlak.

Hier is 'n skakel waarin hierdie: Ongestruktureerd op die verseëlde navraag

Ander wenke

Die antwoord is nee, verseël klasse nie presteer beter as nie-verseël.

Die kwessie kom neer op die call vs callvirt IL op kodes. Call is vinniger as callvirt, en callvirt word hoofsaaklik gebruik wanneer jy weet nie of die voorwerp is subclassed. So mense neem aan dat as jy 'n klas verseël al die op kodes sal verander vanaf calvirts om calls en sal vinniger wees.

Ongelukkig callvirt doen ander dinge wat dit nuttig te maak, soos die nagaan van for verwysings. Dit beteken dat selfs as 'n klas is verseël, die verwysing dalk nog nul wees en dus 'n callvirt nodig. Jy kan kry om hierdie (sonder om die klas te seël), maar dit raak 'n bietjie nutteloos.

Structs gebruik call omdat hulle nie kan subclassed en is nooit nul.

Sien hierdie vraag vir meer inligting:

Bel en callvirt

Update: Vanaf NET Core 2.0 en NET Desktop 4.7.1 Die CLR ondersteun nou devirtualization. Dit kan metodes in verseëlde klasse neem en virtuele oproepe met 'n direkte oproepe te vervang -. En dit kan dit ook doen vir nie-verseël klasse as dit kan uitvind dit is veilig om dit te doen

In so 'n geval ( 'n verseëlde klas wat die CLR kon nie anders op te spoor as veilig om devirtualise), moet 'n verseëlde klas eintlik bied 'n soort van prestasie voordeel.

Dit gesê, sou ek nie dink dit die moeite werd om bekommerd te wees oor tensy jy het al die kode geprofileerde en bepaal dat jy in 'n besonder warm pad toe hy geroep is miljoene keer, of iets wil wees soos dat:

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


Original Antwoord:

Ek het die volgende toets program, en dan decompiled dit met behulp van Reflector om te sien wat MSIL kode uitgelaat.

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 alle gevalle, die C # samesteller (Visual Studio 2010 in Release bou opset) straal identies MSIL, wat soos volg is:

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 dikwels aangehaal rede dat mense sê verseël bied prestasie voordele is dat die samesteller weet die klas is nie oorheers, en dus kan call in plaas van callvirt gebruik as dit nie hoef te gaan vir virtuals, ens As bewys bo, dit is nie waar nie.

My volgende gedagte was dat selfs al is die MSIL is identies, miskien die JIT samesteller lekkernye anders verseël klasse?

Ek het 'n vrylating bou onder die Visual Studio debugger en beskou die decompiled x86 uitset. In beide gevalle, die x86-kode was identies, met die uitsondering van die klas name en funksie geheue adresse (wat natuurlik anders moet wees). Hier is dit

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

Ek het toe gedink miskien hardloop onder die debugger veroorsaak dat dit minder aggressief optimization voer?

Ek het toe gehardloop 'n selfstandige vrylating bou uitvoerbare buite enige debugging omgewings, en gebruik WinDBG + SOS te breek in na die program voltooi het, en kyk na die dissasembly van die net-betyds saamgestel x86 kode.

Soos jy kan sien uit die onderstaande kode, toe hardloop buite die debugger die JIT samesteller is meer aggressief, en dit het die WriteIt metode inlined reguit in die oproeper. Die belangrike ding is egter dat dit identies is wanneer ek bel 'n verseëlde vs-nie verseël klas. Daar is hoegenaamd geen verskil tussen 'n verseëlde of nonsealed klas.

Hier is dit wanneer ek bel 'n normale klas:

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 'n verseëlde klas:

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

Vir my is dit bied soliede bewys dat daar kan nie enige prestasie te verbeter tussen roeping metodes op verseël vs nie-verseël klasse ... Ek dink ek is nou gelukkig: -)

As ek weet, daar is geen waarborg van prestasie voordeel.Maar daar is 'n kans om te afname prestasie straf onder'n spesifieke toestand met verseël metode.(verseël klas maak al die metodes te verseël word.)

Maar dit is aan samesteller implementering en uitvoering omgewing.


Besonderhede

Baie van die moderne CPUs gebruik lang pyplyn struktuur te verhoog prestasie.Omdat die SVE is baie vinniger as die geheue, SVE het om te prefetch kode van geheue te versnel pyplyn.As die kode is nie gereed om op die regte tyd, die pypleidings sal wees ledig.

Daar is'n groot struikelblok genoem dinamiese versending wat ontwrig hierdie "prefetching' optimalisering.Jy kan verstaan as net'n voorwaardelike vertakking.

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

CPU kan nie prefetch volgende kode om uit te voer, in hierdie geval, want die volgende kode posisie is onbekend totdat die toestand is opgelos.So dit maak gevaar oorsake pyplyn ledig.En prestasie straf deur ledig is groot in die gereelde.

Soortgelyke ding gebeur in die geval van die metode oorheersende.Samesteller kan bepaal behoorlike metode oorheersende vir die huidige metode noem, maar soms dit is onmoontlik.In hierdie geval, die korrekte metode bepaal kan word slegs by runtime.Dit is ook'n saak van dinamiese versending, en, 'n vernaamste rede van die dinamies-getik tale is oor die algemeen stadiger as staties-getik tale.

Sommige CPU (insluitend die onlangse Intel se x86 chips) gebruik van tegniek genoem spekulatiewe uitvoering aan te wend pyplyn selfs op die situasie.Net prefetch een van die uitvoering pad.Maar hit rate van hierdie tegniek is nie so hoog nie.En spekulasie mislukking veroorsaak pyplyn stalletjie wat maak ook groot prestasie straf.(dit is heeltemal deur die CPU implementering.sommige mobiele CPU is bekend as nie hierdie soort van die optimalisering om energie te bespaar)

Basies, C# is'n staties saamgestel taal.Maar nie altyd nie.Ek weet nie die presiese toestand en dit is heeltemal aan samesteller implementering.Sommige vertalers kan skakel moontlikheid van dinamiese versending deur die voorkoming metode oorheersende as die metode is gemerk as sealed.Dom opstellers kan nie.Dit is die prestasie tot voordeel van die sealed.


Hierdie antwoord (Hoekom is dit vinniger te verwerk'n gesorteer skikking as'n ongeorden skikking?) is die beskrywing van die tak voorspelling'n baie beter.

Die nasien 'n klas sealed geen prestasie impak moet hê.

Daar is gevalle waar csc dalk 'n callvirt opcode in plaas van 'n call opcode uitstraal. Dit blyk egter sulke gevalle is skaars.

En dit lyk vir my dat die JIT moet in staat wees om dieselfde nie-virtuele funksie oproep uitstraal vir callvirt dat dit sou vir call, indien dit weet dat die klas nie enige subklasse het nie (nog). Indien slegs een implementering van die metode bestaan, is daar geen punt laai sy adres van 'n vtable-net die een implementering direk bel. Vir daardie saak, kan die JIT selfs 'inline die funksie.

Dit is 'n bietjie van 'n waagstuk op deel die JIT se, want as 'n subklas is later gelaai word, sal die JIT het om weg te gooi wat masjienkode en weer saam te stel die kode, afgee 'n ware virtuele call . My raaiskoot is dat dit nie dikwels gebeur in die praktyk.

(En ja, VM ontwerpers regtig aggressief streef hierdie klein prestasie oorwinnings.)

Verseëlde klasse moet bied 'n prestasie te verbeter. Sedert 'n verseëlde klas nie kan afgelei word, kan enige virtuele lede verander word in nie-virtuele lede.

Natuurlik, ons praat baie klein winste. Ek sou nie merk 'n klas as verseël net om 'n prestasie te verbeter kry tensy profilering geopenbaar dit om 'n probleem te wees nie.

I walglik verseël klasse. Selfs al is die prestasie voordele is verstommend (wat ek twyfel), hulle vernietig die objek-georiënteerde model deur te verhoed dat hergebruik via erfenis. Byvoorbeeld, is die onderwerp klas verseël. Terwyl ek kan sien dat 'n mens dalk wil drade om so doeltreffend as moontlik te wees, kan ek ook dink scenario's waar in staat is om Draad oorerf sal groot voordele het. Klas skrywers, as jy moet verseël jou klasse vir "prestasie" redes, verskaf asseblief 'n koppelvlak op die heel minste, sodat ons nie hoef te draai-en-vervang oral wat ons nodig het 'n funksie wat jy vergeet het.

Voorbeeld: SafeThread moes draai die onderwerp klas want onderwerp is verseël en daar is geen IThread koppelvlak; SafeThread vasvang outomaties verwerkte uitsonderings op drade, iets heeltemal ontbreek in die onderwerp klas. [En nee, die verwerkte uitsondering gebeure nie nie optel verwerkte uitsonderings in sekondêre drade].

Ek oorweeg "verseël" klasse die normale geval en ek het nog altyd 'n rede om die "verseël" navraag laat.

Die belangrikste redes vir my is:

a) Beter stel tyd tjeks (beslissende om koppelvlakke nie geïmplementeer sal word waargeneem tydens kompilering, nie net tydens looptyd)

en, bo rede:

b) misbruik van my klasse is nie moontlik dat die pad

Ek wens Microsoft sou gemaak het "verseël" die standaard, nie "onverseëlde".

@Vaibhav, watter soort van toetse het jy uit te voer om te meet prestasie?

Ek dink'n mens sou hê om te gebruik Rotor en om te boor in CLI en verstaan hoe'n verseëlde klas sou prestasie te verbeter.

SSCLI (Rotor)
SSCLI:Gedeelde Bron Gemeenskaplike Taal Infrastruktuur

Die Gemeenskaplike Taal Infrastruktuur (CLI) is die standaard wat ECMA beskryf die kern van die .NETTO Raamwerk.Die Gedeelde Bron CLI (SSCLI), ook bekend as die Rotor, is'n saamgeperste argief van die bron-kode om'n werk implementering van die ECMA CLI en die ECMA C# taal spesifikasie, tegnologie by die hart van Microsoft se .NETTO argitektuur.

verseël klasse sal ten minste 'n klein bietjie vinniger, maar soms kan waayyy vinniger wees ... as die JIT Optimizer oproepe wat anders gewees virtuele oproepe sal kan inline. So, waar daar dikwels genoem metodes wat klein genoeg is om inlined is, beslis oorweeg verseël die klas.

Die beste rede om 'n klas te seël is om te sê "Ek het nie ontwerp hierdie word geërf het uit, so ek gaan nie toelaat dat jy verbrand deur die veronderstelling dat dit is ontwerp om so te wees, en ek m nie van plan om myself te verbrand deur om toegesluit in 'n implementering omdat ek jou laat trek uit dit. "

Ek weet dat sommige hier het gesê hulle haat verseël klasse omdat hulle die geleentheid om te trek uit enigiets wil ... maar dit is dikwels nie die mees onderhoubare keuse ... want bloot 'n klas van slotte afleiding jy in 'n baie meer as nie bloot alles wat. Sy soortgelyk aan en sê: "Ek klasse wat private lede het walglik ... Ek kan dikwels nie die klas te doen wat ek wil, want ek het nie toegang het." Inkapseling is belangrik ... verseëling is een vorm van inkapseling.

Begin hierdie kode en jy sal sien dat verseël klasse is 2 keer vinniger:

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

uitset: Verseëlde klas: 00: 00: 00,1897568 NonSealed klas: 00: 00: 00,3826678

Gelisensieer onder: CC-BY-SA met toeskrywing
Nie verbonde aan StackOverflow
scroll top