L'implementazione di più interfacce su una classe riduce le prestazioni?

2

Ho avuto una domanda riguardante le prestazioni di avere una classe implementare più interfacce. C'è qualche degrado nell'avere una classe implementare 2 interfacce vs 10 interfacce?

Sfondo

Questa domanda deriva dall'indagine su Identity2 di Microsoft. Ho notato che c'erano ~ 6 chiamate DB replicate durante la chiamata di ClaimsIdentityFactory.CreateIdentity() . Dopo ulteriori indagini, sembrava che il codice stesse recuperando l'identità dell'utente più volte durante il controllo di SecurityStamp, ruoli e altre informazioni.

Questo è principalmente dovuto al fatto che UserStore deve gestire SecurityStamp implementando ISecurityStore piuttosto che essere legato al modello TUser o IUser . Mentre capisco perché, non potrebbero non solo controllare se il TUser ha implementato un ISecurityUser e solo prendere quei dati. Soprattutto se ha appena recuperato il TUser poche righe prima.

Potrebbe non essere un grosso problema, ma entra in gioco dato che ho uno script JS che ottiene la mia API per gli aggiornamenti ogni 10 s. Poiché il server su cui sto lavorando non è così potente poiché esegue molti programmi diversi, sto cercando di ridurre il numero di volte in cui il programma attende chiamate di DB ridondanti e rilascia tempo prezioso per la CPU. Attualmente occorrono ~ 0.02 secondi affinché la chiamata si verifichi con ~ 10 chiamate DB. Ma ci sono 5 chiamate DB che ritengo siano ridondanti e la rimozione può aumentare ulteriormente il throughput del mio codice.

    
posta FDaniels 25.08.2017 - 20:34
fonte

2 risposte

5

Di per sé, l'implementazione delle interfacce non ha alcun impatto sulle prestazioni.

Chiamare un metodo attraverso un tipo di interfaccia potrebbe essere un po 'più lento di chiamare un metodo attraverso un tipo di classe, ma i tempi di esecuzione moderni come CLR di Microsoft in grado di eliminare il sovraccarico nella maggior parte dei casi. (Utilizzano una tecnica di ottimizzazione chiamata Inline Cache , che il CLR chiama Virtual Stub Dispatch.)

Il calcolo ripetuto degli stessi dati non ha nulla a che fare con le interfacce e ancor meno con l'efficienza delle interfacce. Questo è un problema di progettazione non correlato.

Ma è vero che l'astrazione attraverso le interfacce potrebbe non essere sempre una soluzione appropriata per un problema di progettazione. Concettualmente, l'uso di un'interfaccia cancella le informazioni sull'oggetto reale. Questo a volte è utile poiché è possibile utilizzare più oggetti in modo intercambiabile se implementano la stessa interfaccia. D'altra parte, diventa più difficile trattare questi oggetti in modo diverso, ad es. per interrogare un oggetto per ulteriori funzionalità. Esprimono un oggetto a un'interfaccia differente è malvista perché questo non è evidente per i chiamanti di tale funzione e non molto estensibile. In alcuni casi i generici sono una soluzione migliore delle interfacce, ma sembrano non avere alcun vantaggio nel contesto della domanda.

Alcuni problemi sono difficili da risolvere bene, quindi optare per una soluzione solida ma meno efficiente sembra legittimo.

    
risposta data 25.08.2017 - 20:57
fonte
0

Risposta breve: no . Il costo della spedizione dinamica non aumenta in quanto una classe implementa più interfacce anche se il compilatore non riesce a essere in linea. C'è un costo per la spedizione dinamica, ma il costo di farlo non si ridimensiona algoritmicamente in nessuna implementazione che abbia mai visto, indipendentemente dal fatto che una classe implementa 1 interfaccia o 100.

Detto questo, ho risposto perché ritengo che tu abbia un problema legato alla progettazione. Quando le prestazioni sono un problema, in realtà credo che dovresti invertire la priorità popolare del design e privilegiare una mentalità orientata ai dati. Ciò inizia con la decisione su come il codice rappresenta, accede e manipola i dati prima di tutto. Le interfacce arrivano seconde. Ciò non significa che si progettano interfacce scadenti che fanno poco più che recuperare e manipolare i dati ed esporre dettagli di implementazione sgradevoli, al contrario. Invece, significa semplicemente che modellerai le cose al livello appropriato di grossolanità o granularità, tenendo in considerazione il modo più efficiente per archiviare, accedere e manipolare i dati.

Per efficienza, spesso devi pensare in un modo più ingombrante, non in modo granulare ( Users , non User , Image , non Pixel ). Spesso non è possibile modellare gli oggetti più giovani e semplici senza dipingersi in un angolo in cui non è più possibile ottimizzare ulteriormente senza fare cascading, interruzione delle modifiche alle interfacce nel sistema.

Come esempio di base, un sistema di particelle veramente interessato all'efficienza come un enorme aspetto della sua qualità non esporrà necessariamente un oggetto scalare Particle che serve come più di un handle per essere esposto e manipolato direttamente a migliaia di diversi clienti in una base di codice complessa. Ciò limiterebbe la capacità di ottimizzare centralmente il codice fino al livello granulare di una singola particella con innumerevoli dipendenze dall'oggetto Particle granulare. Non ti permetterebbe di privilegiare le ottimizzazioni centrali che ti permettono di fare molte cose con più particelle contemporaneamente in parallelo o vettorizzate se tutti i client lavorano con una particella alla volta con un codice scalare a thread singolo. Non consentirebbe di unire e interlacciare i campi di dati di più particelle contemporaneamente, ad esempio, per le rappresentazioni SoA efficienti quando i campi di ogni singola particella devono essere memorizzati all'interno di un oggetto scalare Particle . Sei intrappolato nel regno inefficiente di essere limitato a lavorare con una singola particella AoS in una sola volta e possibilmente con spreco di memoria da cose come padding e campi di dati nascosti per oggetto con un tale disegno. Quindi un tale sistema potrebbe invece esporre un'interfaccia ParticleSystem con funzioni per manipolare molte particelle contemporaneamente anziché una particella alla volta con un'interfaccia scalare Particle .

Una cosa simile per il tuo caso. Se si desidera eliminare molte query di database ridondanti, il punto di partenza per la progettazione deve essere il modo in cui rappresentare, accedere, archiviare e manipolare in modo efficiente i dati in modo tale da eseguire la quantità minima di query. In realtà, suggerirei di farlo inizialmente da un posto centrale alla rinfusa. Le interfacce arrivano seconde. Quando le interfacce vengono prima, spesso si finisce con quel caso in cui i dati sono rappresentati, accessibili e / o manipolati in modo inefficiente a causa del numero di classi granulari che sono potenzialmente ben coordinate in termini di interfacce e di alto livello design ma non in termini di accesso ai dati e modelli di archiviazione.

Va bene se l'efficienza non è un obiettivo primario. In questo caso, dove l'efficienza non è un problema, è sempre possibile fare in modo che l'implementazione dietro le proprie interfacce granulari faccia tutto il necessario per funzionare indipendentemente dal costo computazionale. Tuttavia, quando è una grande preoccupazione, dovresti affrontare i problemi di progettazione con una mentalità più orientata ai dati in modo da non trovarti in quella trappola di inefficienza troppo granulare.

    
risposta data 29.11.2017 - 17:19
fonte