Prestazioni di metodi statici vs metodi di istanza


108

La mia domanda è relativa alle caratteristiche prestazionali dei metodi statici rispetto ai metodi di istanza e alla loro scalabilità. Si supponga per questo scenario che tutte le definizioni di classe siano in un singolo assembly e che siano necessari più tipi di puntatori discreti.

Tener conto di:

public sealed class InstanceClass
{
      public int DoOperation1(string input)
      {
          // Some operation.
      }

      public int DoOperation2(string input)
      {
          // Some operation.
      }

      // … more instance methods.
}

public static class StaticClass
{
      public static int DoOperation1(string input)
      {
          // Some operation.
      }

      public static int DoOperation2(string input)
      {
          // Some operation.
      }

      // … more static methods.
}

Le classi precedenti rappresentano un modello di stile di supporto.

In una classe di istanza, la risoluzione del metodo di istanza richiede alcuni istanti rispetto a StaticClass.

Le mie domande sono:

  1. Quando mantenere lo stato non è un problema (non sono richiesti campi o proprietà), è sempre meglio usare una classe statica?

  2. Dove c'è un numero considerevole di queste definizioni di classi statiche (diciamo 100 ad esempio, con un numero di metodi statici ciascuna) ciò influirà negativamente sulle prestazioni di esecuzione o sul consumo di memoria rispetto allo stesso numero di definizioni di classi di istanza?

  3. Quando viene chiamato un altro metodo all'interno della stessa classe di istanza, la risoluzione dell'istanza si verifica ancora? Ad esempio utilizzando la parola chiave [questo] come this.DoOperation2("abc")dall'interno DoOperation1della stessa istanza.



cosa intendi per "risoluzione dell'istanza"? A livello IL, il puntatore "this" è disponibile come qualsiasi altra variabile locale. In effetti, su alcune vecchie versioni CLR / JIT, potresti chiamare un metodo di istanza su un NULL a condizione che non abbia toccato il 'questo' - il codice è semplicemente volato via e si è schiantato su nulla .. ora CLR / JIT contiene null esplicito- controllare su ogni membro invoca ..
quetzalcoatl

> vijaymukhi.com/documents/books/ilbook/chap8.htm e "call instance" rispetto a "call". Il primo si aspetta "questo" parametro e il secondo no.
quetzalcoatl

@Quetzalcoatl scusa per la confusione, la domanda era più metodo per metodo dalla stessa istanza e se ciò richiede che l'istanza venga risolta da sola.
Bernie White

1
@quetzalcoatl Ho pensato che intendesse, "il compilatore si sbarazza di controllare che thispunti a qualcosa quando una classe chiama un metodo di istanza su se stessa?"
Jon Hanna,

Risposte:


152

In teoria, un metodo statico dovrebbe funzionare leggermente meglio di un metodo di istanza, a parità di altre condizioni, a causa del thisparametro nascosto aggiuntivo .

In pratica, questo fa così poca differenza che sarà nascosto nel rumore delle varie decisioni del compilatore. (Quindi due persone potrebbero "dimostrare" uno migliore dell'altro con risultati in disaccordo). Non da ultimo dal momento che thisnormalmente viene passato in un registro ed è spesso in quel registro per cominciare.

Quest'ultimo punto significa che in teoria, dovremmo aspettarci un metodo statico che prende un oggetto come parametro e fa qualcosa con esso per essere leggermente meno buono dell'equivalente come istanza su quello stesso oggetto. Di nuovo, però, la differenza è così piccola che se provassi a misurarla probabilmente finiresti per misurare qualche altra decisione del compilatore. (Soprattutto perché anche la probabilità che quel riferimento rimanga in un registro per tutto il tempo è piuttosto alta).

Le reali differenze di prestazioni dipenderanno dal fatto che tu abbia artificialmente in memoria oggetti per fare qualcosa che dovrebbe essere naturalmente statico, o che tu stia aggrovigliando catene di oggetti che passano in modi complicati per fare ciò che dovrebbe essere naturalmente un'istanza.

Quindi per il numero 1. Quando mantenere lo stato non è un problema, è sempre meglio essere statici, perché è a questo che serve . Non è un problema di prestazioni, anche se esiste una regola generale per giocare bene con le ottimizzazioni del compilatore: è più probabile che qualcuno si sia dedicato allo sforzo di ottimizzare i casi che si presentano con un uso normale rispetto a quelli che ne escono con un uso strano.

Numero 2. Non fa differenza. C'è una certa quantità di costo per classe per ogni membro che si riferisce sia alla quantità di metadati che ci sono, alla quantità di codice presente nel file DLL o EXE effettivo, sia alla quantità di codice jitted che ci sarà. È lo stesso sia che si tratti di istanza o di statico.

Con l'elemento 3, thisè come thisfa. Tuttavia nota:

  1. Il thisparametro viene passato in un registro particolare. Quando si chiama un metodo di istanza all'interno della stessa classe, probabilmente sarà già in quel registro (a meno che non sia stato nascosto e il registro utilizzato per qualche motivo) e quindi non è richiesta alcuna azione per impostare thisciò su cui deve essere impostato . Ciò si applica in una certa misura, ad esempio, ai primi due parametri del metodo che sono i primi due parametri di una chiamata che effettua.

  2. Poiché sarà chiaro che thisnon è nullo, in alcuni casi potrebbe essere utilizzato per ottimizzare le chiamate.

  3. Poiché sarà chiaro che thisnon è nullo, ciò potrebbe rendere di nuovo più efficienti le chiamate di metodo inline, poiché il codice prodotto per falsificare la chiamata al metodo può omettere alcuni controlli nulli di cui potrebbe comunque aver bisogno.

  4. Detto questo, gli assegni nulli sono economici!

Vale la pena notare che i metodi statici generici che agiscono su un oggetto, piuttosto che i metodi di istanza, possono ridurre alcuni dei costi discussi su http://joeduffyblog.com/2011/10/23/on-generics-and-some-of- gli-associati-overhead / nel caso in cui quel dato statico non è chiamato per un dato tipo. Come dice lui "Per inciso, risulta che i metodi di estensione sono un ottimo modo per rendere le astrazioni generiche più pay-per-play".

Tuttavia, si noti che questo si riferisce solo alla creazione di istanze di altri tipi utilizzati dal metodo, che altrimenti non esistono. In quanto tale, in realtà non si applica a molti casi (qualche altro metodo di istanza ha utilizzato quel tipo, qualche altro codice da qualche altra parte ha utilizzato quel tipo).

Sommario:

  1. Per lo più i costi delle prestazioni dell'istanza rispetto a quella statica sono inferiori a trascurabili.
  2. I costi generalmente vengono quando si abusa dell'elettricità statica, ad esempio o viceversa. Se non fai parte della tua decisione tra statico e istanza, è più probabile che tu ottenga il risultato corretto.
  3. Ci sono rari casi in cui i metodi generici statici in un altro tipo comportano la creazione di un numero inferiore di tipi rispetto ai metodi generici di istanza, che a volte possono far sì che a volte abbia un piccolo vantaggio di essere usati raramente (e "raramente" si riferisce ai tipi con cui è usato nel durata dell'applicazione, non quanto spesso viene chiamata). Una volta capito ciò di cui sta parlando in quell'articolo, vedrai che è comunque irrilevante al 100% per la maggior parte delle decisioni statiche contro istanze. Modifica: e per lo più ha quel costo solo con ngen, non con codice jitted.

Modifica: una nota su quanto siano economici i null-check (che ho affermato sopra). La maggior parte dei controlli null in .NET non verificano affatto la presenza di null, piuttosto continuano ciò che avrebbero fatto supponendo che funzionasse e, se si verifica un'eccezione di accesso, viene trasformata in un file NullReferenceException. Di conseguenza, soprattutto quando concettualmente il codice C # prevede un controllo null perché accede a un membro dell'istanza, il costo se riesce è effettivamente zero. Un'eccezione sarebbe rappresentata da alcune chiamate inline, (perché vogliono comportarsi come se chiamassero un membro dell'istanza) e premono semplicemente un campo per attivare lo stesso comportamento, quindi sono anche molto economiche e possono comunque essere lasciate fuori comunque (es. se il primo passo nel metodo prevedeva l'accesso a un campo così com'era).


Potete commentare se la domanda statica rispetto a quella dell'istanza ha qualche relazione con la coerenza della cache? È più probabile che fare affidamento sull'uno o sull'altro causi errori nella cache? C'è un buon schema che spiega perché?
scriptocalypse

@scriptocalypse Non proprio. La cache delle istruzioni non vedrà alcuna differenza ea quel livello non c'è molta differenza tra l'accesso ai dati tramite thiso tramite un parametro esplicito. Un impatto maggiore qui sarebbe quanto i dati siano vicini ai dati correlati (i campi di tipo valore o i valori di matrice sono più vicini dei dati nei campi di tipo di riferimento) e i modelli di accesso.
Jon Hanna

"in teoria, dovremmo aspettarci che un metodo statico che prende un oggetto come parametro e fa qualcosa con esso sia leggermente meno buono dell'equivalente come istanza su quello stesso oggetto." - Vuoi dire che se il metodo di esempio sopra accetta parametro come oggetto invece di stringa, non statico è meglio? per esempio: ho il mio metodo statico che prende l'oggetto come parametro e lo serializza in stringa e restituisce stringa. stai suggerendo di utilizzare non statico in questo caso?
batmaci

1
@batmaci voglio dire che c'è una buona possibilità che obj.DoSomehting(2)sarebbe leggermente più economico di DoSomething(obj, 2)ma, come ho anche detto, la differenza è così lieve e così dipendente da piccole cose che potrebbero finire diverse nella compilazione finale di cui non vale la pena preoccuparsi affatto. Se stai facendo qualcosa di così costoso (relativamente al tipo di differenze di gioco qui) come serializzare qualcosa su una stringa, allora è particolarmente inutile.
Jon Hanna

C'è una cosa, forse ovvia, ma importante che manca in questa risposta altrimenti eccellente: un metodo di istanza richiede un'istanza e la creazione di un'istanza non è economica. Anche un valore predefinito ctorrichiede ancora l'inizializzazione di tutti i campi. Una volta che hai già un'istanza, questa risposta si applica ("tutte le altre cose sono uguali"). Ovviamente, un cctormetodo costoso può rallentare anche i metodi statici, ma è solo alla prima chiamata e si applica ugualmente ai metodi di istanza. Vedi anche docs.microsoft.com/en-us/previous-versions/dotnet/articles/…
Abel

8

Quando mantenere lo stato non è un problema (non sono richiesti campi o proprietà), è sempre meglio usare una classe statica?

Direi di si. Mentre dichiari qualcosa static, dichiari un intento di esecuzione senza stato (non è obbligatorio, ma un intento di qualcosa che ci si aspetterebbe)

Dove c'è un numero considerevole di queste classi statiche (diciamo 100 ad esempio, con un numero di metodi statici ciascuna) ciò influirà negativamente sulle prestazioni di esecuzione o sul consumo di memoria rispetto allo stesso numero di classi di istanza?

Non pensare così, a meno che tu non sia sicuro che le classi statiche siano davvero stetless, perché in caso contrario è facile rovinare le allocazioni di memoria e ottenere perdite di memoria.

Quando la parola chiave [this] viene utilizzata per chiamare un altro metodo all'interno della stessa classe di istanza, la risoluzione dell'istanza si verifica ancora?

Non sono sicuro, su questo punto (questo è un dettaglio puramente implementativo di CLR), ma penso di sì.


I metodi statici non possono essere derisi, se esegui TDD o anche solo unit test, questo danneggerà molto i tuoi test.
Trampster

@trampster Perché? È solo un pezzo di logica. Puoi facilmente deridere ciò che gli dai? Per ottenere un comportamento corretto. E molti metodi statici saranno comunque parti di logica privata in una funzione.
M. Mimpen

@ M. Mimpen fintanto che lo lasci a piccoli pezzi privati ​​la tua multa, se è un metodo pubblico e lo usi da altre chiusure e devi cambiare quello che fa nel tuo test, allora sei bloccato, cose come l'IO dei file o l'accesso al database o chiamate di rete ecc., Se inserito nel metodo statico diventerà non bloccabile, a meno che, come dici tu, non inietti una dipendenza mockable come parametro al metodo statico
trampster

-2

i metodi statici sono più veloci ma meno OOP, se utilizzerai modelli di progettazione metodo statico probabilmente codice non valido, per scrivere la logica di business meglio senza funzioni statiche e comuni come la lettura di file, WebRequest ecc. meglio realizzare come statiche ... le tue domande non hanno universale risposta


16
Non hai fornito argomenti alle tue affermazioni.
ymajoros

2
@ fjch1997 2 votanti sembrano pensarla diversamente (per quello che vale). Commentare i voti negativi
ymajoros
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.