isbits Union Optimizations

En Julia, le type Array contient à la fois des valeurs "bits" ainsi que des valeurs "boîtées" allouées sur le tas. La distinction réside dans le fait que la valeur elle-même est stockée en ligne (dans la mémoire directement allouée du tableau), ou si la mémoire du tableau est simplement une collection de pointeurs vers des objets alloués ailleurs. En termes de performance, l'accès aux valeurs en ligne est clairement un avantage par rapport à la nécessité de suivre un pointeur vers la valeur réelle. La définition de "isbits" signifie généralement tout type Julia avec une taille fixe et déterminée, ce qui signifie qu'il n'y a pas de champs "pointeur", voir ?isbitstype.

Julia prend également en charge les types Union, littéralement l'union d'un ensemble de types. Les définitions de types Union personnalisés peuvent être extrêmement utiles pour les applications souhaitant "traverser" le système de types nominal (c'est-à-dire les relations de sous-type explicites) et définir des méthodes ou des fonctionnalités sur ces ensembles de types, autrement non liés. Un défi pour le compilateur, cependant, est de déterminer comment traiter ces types Union. L'approche naïve (et en effet, ce que Julia elle-même faisait avant la version 0.7) consiste simplement à créer une "boîte" puis un pointeur dans la boîte vers la valeur réelle, similaire aux valeurs "boîtées" mentionnées précédemment. Cela est malheureux, cependant, en raison du nombre de petits types "bits" primitifs (pensez à UInt8, Int32, Float64, etc.) qui s'intégreraient facilement en ligne dans cette "boîte" sans nécessiter d'indirection pour l'accès à la valeur. Il existe deux principales façons dont Julia peut tirer parti de cette optimisation depuis la version 0.7 : les champs Union isbits dans les types et les tableaux Union isbits.

isbits Union Structs

Julia inclut désormais une optimisation dans laquelle les champs "isbits Union" dans les types (mutable struct, struct, etc.) seront stockés en ligne. Cela est réalisé en déterminant la "taille en ligne" du type Union (par exemple, Union{UInt8, Int16} aura une taille de deux octets, ce qui représente la taille nécessaire du plus grand type Union Int16), et en outre, en allouant un octet supplémentaire de "tag de type" (UInt8), dont la valeur signale le type de la valeur réelle stockée en ligne dans les "octets Union". La valeur de l'octet de tag de type est l'index du type de la valeur réelle dans l'ordre des types du type Union. Par exemple, une valeur de tag de type de 0x02 pour un champ de type Union{Nothing, UInt8, Int16} indiquerait qu'une valeur Int16 est stockée dans les 16 bits du champ dans la mémoire de la structure ; une valeur de 0x01 indiquerait qu'une valeur UInt8 a été stockée dans les premiers 8 bits des 16 bits de la mémoire du champ. Enfin, une valeur de 0x00 signale que la valeur nothing sera retournée pour ce champ, même si, en tant que type singleton avec une seule instance de type, il a techniquement une taille de 0. L'octet de tag de type pour un champ Union d'un type est stocké directement après la mémoire Union calculée du champ.

isbits Union Memory

Julia peut désormais également stocker des valeurs "isbits Union" en ligne dans une mémoire, au lieu de nécessiter une boîte d'indirection. L'optimisation est réalisée en stockant une mémoire de "tag de type" supplémentaire de bytes, un byte par élément, aux côtés des bytes des données réelles. Cette mémoire de tag de type remplit la même fonction que le champ de type : sa valeur signale le type de la valeur Union réellement stockée. La "mémoire de tag de type" suit directement l'espace de données régulier. Ainsi, la formule pour accéder aux bytes de tag de type d'un tableau isbits Union est a->data + a->length * a->elsize.