isbits Union Optimizations

En Julia, el tipo Array contiene tanto valores "bits" como valores "en caja" asignados en el heap. La distinción es si el valor en sí se almacena en línea (en la memoria directamente asignada del array), o si la memoria del array es simplemente una colección de punteros a objetos asignados en otro lugar. En términos de rendimiento, acceder a valores en línea es claramente una ventaja sobre tener que seguir un puntero al valor real. La definición de "isbits" generalmente significa cualquier tipo de Julia con un tamaño fijo y determinado, lo que significa que no hay campos de "puntero", consulta ?isbitstype.

Julia también admite tipos de unión, literalmente la unión de un conjunto de tipos. Las definiciones de tipos de unión personalizados pueden ser extremadamente útiles para aplicaciones que deseen "cruzar" el sistema de tipos nominal (es decir, relaciones de subtipos explícitas) y definir métodos o funcionalidades sobre estos conjuntos de tipos, que de otro modo estarían no relacionados. Sin embargo, un desafío para el compilador es determinar cómo tratar estos tipos de unión. El enfoque ingenuo (y de hecho, lo que Julia mismo hizo antes de la versión 0.7) es simplemente hacer una "caja" y luego un puntero en la caja al valor real, similar a los valores "en caja" mencionados anteriormente. Esto es desafortunado, sin embargo, debido al número de tipos "bits" pequeños y primitivos (piensa en UInt8, Int32, Float64, etc.) que fácilmente podrían caber en línea en esta "caja" sin necesidad de ninguna indirecta para el acceso al valor. Hay dos formas principales en que Julia puede aprovechar esta optimización a partir de la versión 0.7: campos de unión isbits en tipos y arreglos de unión isbits.

isbits Union Structs

Julia ahora incluye una optimización en la que los campos de "Union isbits" en tipos (mutable struct, struct, etc.) se almacenarán en línea. Esto se logra determinando el "tamaño en línea" del tipo Union (por ejemplo, Union{UInt8, Int16} tendrá un tamaño de dos bytes, que representa el tamaño necesario del tipo Union más grande Int16), y además, asignando un "byte de etiqueta de tipo" adicional (UInt8), cuyo valor indica el tipo del valor real almacenado en línea de los "bytes de Union". El valor del byte de etiqueta de tipo es el índice del tipo del valor real en el orden de tipos del tipo Union. Por ejemplo, un valor de etiqueta de tipo de 0x02 para un campo con tipo Union{Nothing, UInt8, Int16} indicaría que un valor Int16 se almacena en los 16 bits del campo en la memoria de la estructura; un valor de 0x01 indicaría que un valor UInt8 se almacenó en los primeros 8 bits de los 16 bits de la memoria del campo. Por último, un valor de 0x00 indica que se devolverá el valor nothing para este campo, aunque, como un tipo singleton con una única instancia de tipo, técnicamente tiene un tamaño de 0. El byte de etiqueta de tipo para un campo Union de un tipo se almacena directamente después de la memoria Union calculada del campo.

isbits Union Memory

Julia ahora también puede almacenar valores de "isbits Union" en línea en una Memoria, en lugar de requerir una caja de indireccionamiento. La optimización se logra almacenando una "memoria de etiqueta de tipo" adicional de bytes, un byte por elemento, junto a los bytes de los datos reales. Esta memoria de etiqueta de tipo cumple la misma función que el campo de tipo: su valor señala el tipo del valor de Union almacenado realmente. La "memoria de etiqueta de tipo" sigue directamente al espacio de datos regular. Así que la fórmula para acceder a los bytes de etiqueta de tipo de un Array de isbits Union es a->data + a->length * a->elsize.