Compartilhar via


Desempenho da Integração CLR

Este tópico discute algumas das opções de design que aprimoram o desempenho da integração do Microsoft SQL Server com o CLR (Common Language Runtime) do Microsoft .NET Framework.

O processo de compilação

Durante a compilação de expressões SQL, quando uma referência a uma rotina gerenciada é encontrada, um stub de MSIL (linguagem intermediária) da Microsoft é gerado. Esse stub inclui código para empacotar os parâmetros de rotina do SQL Server para o CLR, invocar a função e retornar o resultado. Esse código "glue" é baseado no tipo de parâmetro e na direção do parâmetro (dentro, fora ou referência).

O código cola permite otimizações específicas de tipo e garante a imposição eficiente da semântica do SQL Server, como nulidade, facetas restritivas, por valor e tratamento de exceção padrão. Ao gerar código para os tipos exatos dos argumentos, você evita a coerção de tipos ou custos com a criação de objetos wrapper (o chamado "boxing") além do limite de invocação.

Em seguida, o stub gerado é compilado para código nativo e otimizado para a arquitetura de hardware específica na qual o SQL Server é executado, usando os serviços de compilação JIT (just-in-time) do CLR. Os serviços JIT são invocados no nível do método e permitem que o ambiente de hospedagem do SQL Server crie uma única unidade de compilação que abrange a execução do SQL Server e do CLR. Depois que o stub é compilado, o ponteiro de função resultante se torna a implementação em tempo de execução da função. Essa abordagem de geração de código garante que não haja custos adicionais de invocação relacionados ao acesso de reflexão ou metadados em tempo de execução.

Transições rápidas entre o SQL Server e o CLR

O processo de compilação gera um ponteiro de função que pode ser chamado em tempo de execução a partir do código nativo. No caso de funções definidas pelo usuário com valor escalar, essa invocação de função ocorre por linha. Para minimizar o custo de transição entre o SQL Server e o CLR, as instruções que contêm qualquer invocação gerenciada têm uma etapa de inicialização para identificar o domínio do aplicativo de destino. Essa etapa de identificação reduz o custo de transição de cada linha.

Considerações sobre desempenho

O seguinte resume as considerações de desempenho específicas à integração clr no SQL Server. Informações mais detalhadas podem ser encontradas em "Usando a integração clr no SQL Server 2005" no site do MSDN. Informações gerais sobre o desempenho do código gerenciado podem ser encontradas em "Aprimorando o desempenho e a escalabilidade do aplicativo .NET" no site do MSDN.

Funções definidas pelo usuário

As funções CLR se beneficiam de um caminho de invocação mais rápido do que o de Transact-SQL funções definidas pelo usuário. Além disso, o código gerenciado tem uma vantagem de desempenho decisiva sobre o Transact-SQL em termos de código de procedimento, computação e manipulação de cadeia de caracteres. As funções CLR que têm uso intensivo de computação e que não executam o acesso a dados são melhor gravadas no código gerenciado. No entanto, as funções Transact-SQL executam o acesso a dados com mais eficiência do que a integração CLR.

Agregações definidas pelo usuário

O código gerenciado pode ter um desempenho significativamente melhor que a agregação baseada em cursor. O código gerenciado geralmente é executado um pouco mais lentamente do que as funções agregadas internas do SQL Server. Se houver uma função de agregação interna nativa, é recomendável utilizá-la. Nos casos em que a agregação necessária não tem suporte nativo, considere uma agregação definida pelo usuário CLR em uma implementação baseada em cursor por motivos de desempenho.

Funções de Table-Valued de streaming

Frequentemente, os aplicativos precisam retornar uma tabela como resultado da invocação de uma função. Exemplos incluem a leitura de dados tabulares de um arquivo como parte de uma operação de importação e a conversão de valores separados por vírgula em uma representação relacional. Normalmente, isso pode ser feito materializando e preenchendo a tabela de resultados antes de ela poder ser consumida pelo chamador. A integração do CLR no SQL Server introduz um novo mecanismo de extensibilidade chamado STVF (função com valor de tabela de streaming). As STVFs gerenciadas têm um desempenho melhor que o de implementações de procedimentos armazenados estendidos comparáveis.

As STVFs são funções gerenciadas que retornam uma interface IEnumerable. A IEnumerable tem métodos para navegar pelo conjunto de resultados retornado pela STVF. Quando a STVF é invocada, a IEnumerable retornada é conectada diretamente ao plano de consulta. O plano de consulta chamará métodos de IEnumerable quando for necessário buscar linhas. Esse modelo de iteração permite que os resultados sejam consumidos imediatamente depois que a primeira linha é gerada, em vez de aguardar até que toda a tabela seja preenchida. Ele também reduz significativamente a memória consumida ao invocar a função.

Matrizes versus cursores

Quando os cursores Transact-SQL devem percorrer dados que são mais facilmente expressos como uma matriz, o código gerenciado pode ser usado com ganhos significativos de desempenho.

Dados de cadeia de caracteres

Dados de caracteres do SQL Server, como varchar, podem ser do tipo SqlString ou SqlChars em funções gerenciadas. As variáveis SqlString criam uma instância do valor inteiro na memória. As variáveis sqlChars fornecem uma interface de streaming que pode ser usada para obter melhor desempenho e escalabilidade, não criando uma instância do valor inteiro na memória. Isso se torna particularmente importante para dados lob (objeto grande). Além disso, os dados XML do servidor podem ser acessados por meio de uma interface de streaming retornada por SqlXml.CreateReader().

CLR vs. Procedimentos Armazenados Estendidos

As APIs (interfaces de programação de aplicativo) do Microsoft.SqlServer.Server que permitem que os procedimentos gerenciados enviem conjuntos de resultados de volta ao cliente têm um desempenho melhor do que as APIs do ODS (Open Data Services) usadas por procedimentos armazenados estendidos. Além disso, as APIs System.Data.SqlServer dão suporte a tipos de dados como xml, varchar(max), nvarchar(max)e varbinary(max), introduzidos no SQL Server 2005 (9.x), enquanto as APIs do ODS não foram estendidas para dar suporte aos novos tipos de dados.

Com o código gerenciado, o SQL Server gerencia o uso de recursos como memória, threads e sincronização. Isso ocorre porque as APIs gerenciadas que expõem esses recursos são implementadas sobre o gerenciador de recursos do SQL Server. Por outro lado, o SQL Server não tem exibição ou controle sobre o uso de recursos do procedimento armazenado estendido. Por exemplo, se um procedimento armazenado estendido consumir muitos recursos de CPU ou memória, não haverá como detectar ou controlar isso com o SQL Server. Com o código gerenciado, no entanto, o SQL Server pode detectar que um determinado thread não rendeu por um longo período de tempo e, em seguida, forçar a tarefa a produzir para que outro trabalho possa ser agendado. Consequentemente, o uso de código gerenciado fornece melhor escalabilidade e uso de recursos do sistema.

O código gerenciado pode incorrer em sobrecarga adicional necessária para manter o ambiente de execução e executar verificações de segurança. Esse é o caso, por exemplo, quando a execução dentro do SQL Server e várias transições de código gerenciado para nativo são necessárias (porque o SQL Server precisa fazer manutenção adicional em configurações específicas de thread ao migrar para o código nativo e voltar). Consequentemente, os procedimentos armazenados estendidos podem superar significativamente o código gerenciado em execução no SQL Server para casos em que há transições frequentes entre código gerenciado e nativo.

Observação

É recomendável que você não desenvolva novos procedimentos armazenados estendidos, pois esse recurso foi preterido.

Serialização nativa para tipos de User-Defined

Os UDTs (tipos definidos pelo usuário) são criados como um mecanismo de extensibilidade para o sistema de tipo de escalar. O SQL Server implementa um formato de serialização para UDTs chamado Format.Native. Durante a compilação, a estrutura do tipo é examinada para gerar o MSIL personalizado para essa definição de tipo específica.

A serialização nativa é a implementação padrão do SQL Server. A serialização definida pelo usuário invoca um método definido pelo autor do tipo para fazer a serialização. A serialização Format.Native dever ser usada quando possível para obter um melhor desempenho.

Normalização de UDTs comparáveis

As operações relacionais, como a classificação e a comparação de UDTs, funcionam diretamente na representação binária do valor. Isto é realizado armazenando uma representação normalizada (em ordem binária) do estado do UDT no disco.

A normalização tem dois benefícios: torna a operação de comparação consideravelmente menos cara, evitando a construção da instância de tipo e a sobrecarga de invocação do método; e cria um domínio binário para a UDT, permitindo a construção de histogramas, índices e histogramas para valores do tipo. Consequentemente, as UDTs normalizadas têm um perfil de desempenho muito semelhante aos tipos internos nativos para operações que não envolvem invocação de método.

Uso escalonável de memória

Para que a coleta de lixo gerenciada seja bem executada e dimensionada no SQL Server, evite uma alocação grande e única. Alocações maiores que 88 quilobytes (KB) de tamanho serão colocadas no Heap de Objetos Grandes, o que fará com que a coleta de lixo seja executada e dimensione muito pior do que muitas alocações menores. Por exemplo, se você precisar alocar uma grande matriz multidimensional, é melhor alocar uma matriz irregular (dispersa).

Consulte Também

Tipos de User-Defined CLR