Compartir vía


Rendimiento de la arquitectura de integración clR

Se aplica a: SQL Server Azure SQL Managed Instance

En este artículo se describen algunas de las opciones de diseño que mejoran el rendimiento de la integración de SQL Server con Common Language Runtime (CLR) de Microsoft .NET Framework.

Proceso de compilación

Durante la compilación de expresiones SQL, cuando se encuentra una referencia a una rutina administrada, se genera un código auxiliar de lenguaje intermedio de Microsoft (MSIL). Este código auxiliar incluye código para serializar los parámetros rutinarios de SQL Server a CLR, invocar la función y devolver el resultado. Este código de "unión" está basado en el tipo y la dirección del parámetro (entrada, salida o referencia).

El código de pegamento habilita optimizaciones específicas del tipo y garantiza una aplicación eficaz de la semántica de SQL Server, como la nulabilidad, las facetas restringidas, el control de excepciones estándar y por valor. Al generar código para los tipos exactos de los argumentos, se evitan los costos de la conversión de tipos o de la creación de objetos contenedores (denominada "conversión boxing") dentro de los límites de la invocación.

A continuación, el código auxiliar generado se compila en código nativo y se optimiza para la arquitectura de hardware concreta en la que se ejecuta SQL Server, mediante los servicios de compilación Just-In-Time (JIT) de CLR. Los servicios JIT se invocan en el nivel de método y permiten que el entorno de hospedaje de SQL Server cree una sola unidad de compilación que abarque la ejecución de SQL Server y CLR. Una vez compilado el código auxiliar, el puntero resultante a la función se convierte en la implementación de la función durante la ejecución. Este enfoque de generación de código garantiza que no haya costos de invocación adicionales relacionados con el acceso de reflexión o metadatos en tiempo de ejecución.

Transiciones rápidas entre SQL Server y CLR

El proceso de compilación produce un puntero a función al que se puede llamar durante la ejecución desde código nativo. En el caso de las funciones definidas por el usuario con valores escalares, esta invocación de función se produce por fila. Para minimizar el costo de la transición entre SQL Server y CLR, las instrucciones que contienen cualquier invocación administrada tienen un paso de inicio para identificar el dominio de aplicación de destino. Este paso de identificación reduce el costo de la transición para cada fila.

Consideraciones sobre el rendimiento

En la sección siguiente se resumen las consideraciones de rendimiento específicas de la integración de CLR en SQL Server. Para obtener más información, vea Uso de la integración clR en SQL Server 2005. Para obtener información sobre el rendimiento del código administrado, consulte Mejora del rendimiento y la escalabilidad de las aplicaciones .NET.

Funciones definidas por el usuario

Las funciones CLR se benefician de una ruta de invocación más rápida que las funciones definidas por el usuario de Transact-SQL. Además, el código administrado tiene una ventaja decisiva de rendimiento sobre Transact-SQL en términos de código de procedimientos, cálculo y manipulación de cadenas. Las funciones CLR que consumen muchos procesos y que no realizan el acceso a datos se escriben mejor en código administrado. Sin embargo, las funciones de Transact-SQL realizan el acceso a datos de forma más eficaz que la integración de CLR.

Agregados definidos por el usuario

El código administrado puede mejorar significativamente el rendimiento de la agregación basada en cursor. Por lo general, el código administrado funciona ligeramente más lento que las funciones de agregado integradas de SQL Server. Si existe una función de agregado integrada nativa, se recomienda utilizarla. En los casos en los que no se admite de forma nativa la agregación necesaria, considere la posibilidad de agregar un agregado definido por el usuario clR a través de una implementación basada en cursores por motivos de rendimiento.

Funciones con valores de tabla de streaming

Después de invocar una función, las aplicaciones suelen necesitar devolver una tabla. Entre los ejemplos se incluye la lectura de datos tabulares de un archivo como parte de una operación de importación y la conversión de valores separados por comas en una representación relacional. Normalmente, esto se puede llevar a cabo materializando y rellenando la tabla de resultados antes de que pueda consumirla el autor de las llamadas. La integración de CLR en SQL Server presenta un nuevo mecanismo de extensibilidad denominado función con valores de tabla de streaming (STVF). Las STVF administradas funcionan mejor que las implementaciones de procedimiento almacenado extendido comparables.

Las STVF son funciones administradas que devuelven una interfaz IEnumerable. IEnumerable tiene métodos para navegar por el conjunto de resultados devuelto por la STVF. Cuando se invoca la STVF, la interfaz IEnumerable devuelta se conecta directamente al plan de consulta. El plan de consulta llama a los métodos IEnumerable cuando necesita capturar filas. Este modelo de iteración permite utilizar los resultados inmediatamente después de que se genere la primera fila, en lugar de esperar hasta que se rellene la tabla completa. Reduce también significativamente la cantidad de memoria que se utiliza al invocar la función.

Matrices frente a cursores

Cuando los cursores de Transact-SQL deben atravesar los datos que se expresan más fácilmente como una matriz, el código administrado se puede usar con importantes mejoras de rendimiento.

Datos de cadena

Los datos de caracteres de SQL Server, como varchar, pueden ser del tipo SqlString o SqlChars en funciones administradas. Las variables SqlString crean una instancia del valor completo en la memoria. Las variables SqlChars proporcionan una interfaz de transmisión por secuencias que se puede utilizar para lograr mejores rendimiento y escalabilidad sin crear una instancia del valor completo en la memoria. Esto se convierte en importante para los datos de objetos grandes (LOB). Además, se puede tener acceso a los datos XML del servidor a través de una interfaz de transmisión por secuencias devuelta por SqlXml.CreateReader().

CLR frente a procedimientos almacenados extendidos

Las Microsoft.SqlServer.Server interfaces de programación de aplicaciones (API) que permiten a los procedimientos administrados enviar conjuntos de resultados de vuelta al cliente funcionan mejor que las API de Open Data Services (ODS) usadas por procedimientos almacenados extendidos. Además, las API System.Data.SqlServer admiten tipos de datos como xml, varchar(max), nvarchar(max)y varbinary(max), introducidos en SQL Server 2005 (9.x), mientras que las API ODS no se han ampliado para admitir los nuevos tipos de datos.

Con el código administrado, SQL Server administra el uso de recursos como memoria, subprocesos y sincronización. Esto se debe a que las API administradas que exponen estos recursos se implementan sobre el administrador de recursos de SQL Server. Por el contrario, SQL Server no tiene ninguna vista ni control sobre el uso de recursos del procedimiento almacenado extendido. Por ejemplo, si un procedimiento almacenado extendido consume demasiados recursos de CPU o memoria, no hay forma de detectarlo ni controlarlo con SQL Server. Sin embargo, con código administrado, SQL Server puede detectar que un subproceso determinado no ha producido durante un largo período de tiempo y, a continuación, forzar la tarea a producir para que se pueda programar otro trabajo. Por lo tanto, el uso de código administrado proporciona una mejor escalabilidad y uso de recursos del sistema.

El código administrado puede incurrir en una sobrecarga adicional necesaria para mantener el entorno de ejecución y realizar comprobaciones de seguridad. Este es el caso, por ejemplo, cuando se ejecuta dentro de SQL Server y se requieren numerosas transiciones de código administrado a nativo (ya que SQL Server debe realizar un mantenimiento adicional en la configuración específica del subproceso al salir al código nativo y volver). Por lo tanto, los procedimientos almacenados extendidos pueden superar significativamente el código administrado que se ejecuta en SQL Server para los casos en los que hay transiciones frecuentes entre código administrado y nativo.

Nota:

No desarrolle nuevos procedimientos almacenados extendidos, ya que esta característica está en desuso.

Serialización nativa para tipos definidos por el usuario

Los tipos definidos por el usuario (UDT) están diseñados como un mecanismo de extensibilidad para el sistema de tipo escalar. SQL Server implementa un formato de serialización para udT denominado Format.Native. Durante la compilación se examina la estructura del tipo para generar el MSIL personalizado para esa definición de tipo concreta.

La serialización nativa es la implementación predeterminada de SQL Server. La serialización definida por el usuario invoca un método definido por el autor de tipo para realizar la serialización. Se debe utilizar la serialización Format.Native siempre que sea posible para obtener el máximo rendimiento.

Normalización de UDT comparables

Las operaciones relacionales, como ordenar y comparar tipos definidos por el usuario (UDT), funcionan directamente en la representación binaria del valor. Esto se logra almacenando en disco una representación normalizada (ordenada de forma binaria) del estado del UDT.

La normalización tiene dos ventajas:

  • hace que la operación de comparación sea considerablemente menos costosa evitando la construcción de la instancia de tipo y la sobrecarga de invocación del método.

  • crea un dominio binario para el UDT, lo que permite la construcción de histogramas, índices y histogramas para los valores del tipo.

Por lo tanto, los UDT normalizados tienen un perfil de rendimiento similar a los tipos integrados nativos para las operaciones que no implican invocación de método.

Uso escalable de memoria

Para que la recolección de elementos no utilizados administrados funcione y escale bien en SQL Server, evite una asignación grande y única. Las asignaciones mayores de 88 kilobytes (KB) de tamaño se colocan en el montón de objetos grandes, lo que hace que la recolección de elementos no utilizados se realice y escale peor que muchas asignaciones más pequeñas. Por ejemplo, si necesita asignar una matriz multidimensional grande, es mejor asignar una matriz escalonada (dispersa).