Share via


Architettura di integrazione CLR - Prestazioni

Si applica a: SQL Server Istanza gestita di SQL di Azure

Questo argomento illustra alcune delle scelte di progettazione che migliorano le prestazioni dell'integrazione di Microsoft SQL Server con Microsoft .NET Framework Common Language Runtime (CLR).

Processo di compilazione

Durante la compilazione di espressioni SQL, quando viene rilevato un riferimento a una routine gestita, viene generato uno stub di Microsoft Intermediate Language (MSIL). Questo stub include il codice per eseguire il marshalling dei parametri di routine da SQL Server a CLR, richiamare la funzione e restituire il risultato. Questo codice di "unione" si basa sul tipo di parametro e sulla direzione del parametro (interna, esterna o di riferimento).

Il codice di associazione consente ottimizzazioni specifiche del tipo e garantisce un'applicazione efficiente della semantica SQL Server, ad esempio nullabilità, vincoli di facet, per valore e gestione delle eccezioni standard. La generazione di codice apposito per i tipi degli argomenti consente di evitare i costi legati all'assegnazione forzata o alla creazione di oggetti wrapper, chiamata "boxing", all'interno dei limiti della chiamata.

Lo stub generato viene quindi compilato in codice nativo e ottimizzato per l'architettura hardware specifica in cui viene eseguita SQL Server, usando i servizi di compilazione JIT (just-in-time) di CLR. I servizi JIT vengono richiamati a livello di metodo e consentono all'ambiente di hosting SQL Server di creare un'unica unità di compilazione che si estende sia SQL Server che l'esecuzione CLR. Dopo aver compilato lo stub, il puntatore a funzione risultante diventa l'implementazione in fase di esecuzione della funzione. Questo approccio che prevede la generazione di codice consente di evitare i costi di chiamata aggiuntivi relativi alla riflessione o all'accesso ai metadati in fase di esecuzione.

Transizioni veloci tra SQL Server e CLR

Il processo di compilazione restituisce un puntatore a funzione che può essere chiamato in fase di esecuzione dal codice nativo. Nel caso di funzioni definite dall'utente a valori scalari, questa chiamata alla funzione avviene su ogni riga. Per ridurre al minimo il costo della transizione tra SQL Server e CLR, le istruzioni che contengono qualsiasi chiamata gestita hanno un passaggio di avvio per identificare il dominio dell'applicazione di destinazione. Questo passaggio di identificazione riduce il costo della transizione per ogni riga.

Considerazioni sulle prestazioni

Di seguito vengono riepilogate le considerazioni sulle prestazioni specifiche dell'integrazione CLR in SQL Server. Altre informazioni dettagliate sono disponibili in "Uso dell'integrazione CLR in SQL Server 2005" nel sito Web MSDN. Informazioni generali sulle prestazioni del codice gestito sono disponibili in "Miglioramento delle prestazioni e della scalabilità dell'applicazione .NET" nel sito Web MSDN.

Funzioni definite dall'utente

Le funzioni CLR possono trarre vantaggio da un percorso di chiamata più rapido rispetto a quello delle funzioni definite dall'utente Transact-SQL. Inoltre, il codice gestito offre un vantaggio decisivo sulle prestazioni rispetto a Transact-SQL in termini di codice procedurale, calcolo e manipolazione delle stringhe. Le funzioni CLR che prevedono intense attività di calcolo e che non eseguono l'accesso ai dati vengono scritte meglio in codice gestito. Le funzioni Transact-SQL, tuttavia, eseguono l'accesso ai dati in modo più efficiente rispetto all'integrazione CLR.

Funzioni di aggregazione definite dall'utente

Il codice gestito può determinare prestazioni notevolmente superiori rispetto all'aggregazione basata sul cursore. Il codice gestito esegue in genere un codice leggermente più lento rispetto alle funzioni di aggregazione predefinite SQL Server. Se esiste una funzione di aggregazione predefinita nativa, è consigliabile utilizzarla. Nei casi in cui l'aggregazione necessaria non è supportata a livello nativo, è opportuno utilizzare un'aggregazione definita dall'utente CLR su un'implementazione basata sul cursore per migliorare le prestazioni.

STVF (Streaming Table-Valued Function, Funzioni di flusso con valori di tabella)

Le applicazioni spesso devono restituire una tabella come risultato della chiamata di una funzione. Gli esempi includono la lettura di dati tabulari da un file nell'ambito di un'operazione di importazione e la conversione di valori delimitati da virgole in una rappresentazione relazionale. Per effettuare queste operazioni in genere è necessario materializzare e popolare la tabella dei risultati prima che possa essere utilizzata dal chiamante. L'integrazione di CLR in SQL Server introduce un nuovo meccanismo di estendibilità denominato funzione con valori di tabella di streaming (STVF). Le funzioni di flusso con valori di tabella offrono prestazioni migliori rispetto alle implementazioni delle stored procedure estese confrontabili.

STVFs sono funzioni gestite che restituiscono un'interfaccia IEnumerable . IEnumerable include metodi per esplorare il set di risultati restituito da STVF. Quando viene richiamato stVF, l'IEnumerable restituito è direttamente connesso al piano di query. Il piano di query chiama metodi IEnumerable quando deve recuperare righe. Questo modello di iterazione consente di utilizzare immediatamente i risultati subito dopo la produzione della prima riga, anziché dover attendere che venga popolata l'intera tabella. Riduce inoltre significativamente la quantità di memoria utilizzata quando si richiama la funzione.

Confronto tra matrici e cursori

Quando i cursori Transact-SQL devono attraversare i dati che sono più facilmente espressi come matrice, il codice gestito può essere usato con miglioramenti significativi sulle prestazioni.

Dati di tipo stringa

SQL Server dati di carattere, ad esempio varchar, può essere del tipo SqlString o SqlChars nelle funzioni gestite. Le variabili SqlString creano un'istanza dell'intero valore in memoria. Le variabili SqlChars forniscono un'interfaccia di flusso che può essere utilizzata per ottenere prestazioni migliori e una maggiore scalabilità creando un'istanza dell'intero valore in memoria. Questo diventa particolarmente importante per i dati di tipo LOB. È inoltre possibile accedere ai dati XML del server tramite un'interfaccia di streaming restituita da SqlXml.CreateReader().

Confronto tra CLR e stored procedure estese

Le API Microsoft.SqlServer.Server che consentono alle procedure gestite di inviare di nuovo i set di risultati al client offrono prestazioni migliori rispetto alle API ODS (Open Data Services) utilizzate dalle stored procedure estese. Inoltre, le API System.Data.SqlServer supportano tipi di dati, ad esempio xml, varchar(max), nvarchar(max)e varbinary(max), introdotti in SQL Server 2005 (9.x), mentre le API ODS non sono state estese per supportare i nuovi tipi di dati.

Con il codice gestito, SQL Server gestisce l'uso di risorse, ad esempio memoria, thread e sincronizzazione. Ciò avviene perché le API gestite che espongono queste risorse vengono implementate sopra SQL Server resource manager. Al contrario, SQL Server non ha visualizzazione o controllo sull'utilizzo delle risorse della stored procedure estesa. Ad esempio, se una stored procedure estesa usa troppe risorse di CPU o memoria, non è possibile rilevare o controllare questa operazione con SQL Server. Con il codice gestito, tuttavia, SQL Server può rilevare che un determinato thread non ha restituito per un lungo periodo di tempo e quindi forzare l'attività a restituire in modo che sia possibile pianificare altri lavori. Di conseguenza, l'utilizzo di codice gestito offre una maggiore scalabilità e un miglior utilizzo delle risorse di sistema.

Il codice gestito può determinare un overhead aggiuntivo, necessario per gestire l'ambiente di esecuzione ed eseguire controlli di sicurezza. Questo è il caso, ad esempio, quando viene eseguito all'interno di SQL Server e numerose transizioni dal codice gestito al codice nativo sono necessari (perché SQL Server deve eseguire ulteriori operazioni di manutenzione sulle impostazioni specifiche del thread durante lo spostamento nel codice nativo e indietro). Di conseguenza, le stored procedure estese possono eseguire in modo significativo il codice gestito in esecuzione all'interno di SQL Server per i casi in cui sono presenti transizioni frequenti tra codice gestito e nativo.

Nota

È consigliabile non sviluppare nuove stored procedure estese, in quanto questa caratteristica è deprecata.

Serializzazione nativa per i tipi definiti dall'utente

I tipi definiti dall'utente sono progettati come un meccanismo di extensibility per il sistema di tipo scalare. SQL Server implementa un formato di serializzazione per le UDT denominate Format.Native. Durante la compilazione, la struttura del tipo viene esaminata per generare un codice MSIL personalizzato per la definizione del tipo specifico.

La serializzazione nativa è l'implementazione predefinita per SQL Server. La serializzazione definita dall'utente richiama un metodo definito dall'autore del tipo per eseguire la serializzazione. La serializzazione Format.Native deve essere usata quando possibile per ottenere prestazioni ottimali.

Normalizzazione dei tipi definiti dall'utente confrontabili

Le operazioni relazionali, ad esempio l'ordinamento e il confronto dei tipi definiti dall'utente, agiscono direttamente sulla rappresentazione binaria del valore. A tale scopo, è necessario archiviare una rappresentazione normalizzata (con ordinamento binario) dello stato del tipo definito dall'utente su disco.

La normalizzazione presenta due vantaggi: rende l'operazione di confronto molto meno costosa evitando la costruzione dell'istanza del tipo e l'overhead della chiamata al metodo e crea un dominio binario per il tipo definito dall'utente, consentendo la costruzione di istogrammi, indici e istogrammi per i valori del tipo. Di conseguenza, i tipi definiti dall'utente normalizzati offrono prestazioni molto simili ai tipi predefiniti nativi per operazioni che non comportano chiamate al metodo.

Utilizzo della memoria scalabile

Per consentire l'esecuzione e la scalabilità di Garbage Collection gestita in SQL Server, evitare un'allocazione singola e di grandi dimensioni. Le allocazioni di dimensioni maggiori di 88 kilobyte (KB) vengono inserite nell'heap oggetti grandi che determina prestazioni e scalabilità nettamente inferiori rispetto ad allocazioni più piccole. Se ad esempio è necessario allocare una matrice multidimensionale di dimensioni elevate, è preferibile allocare una matrice di matrici (a dispersione).

Vedere anche

Tipi CLR definiti dall'utente