Оптимизации объединения isbits
В Julia тип Array
хранит как битовые значения, так и выделенные в куче упакованные значения. Различие заключается в том, хранится ли само значение как встроенное (в непосредственно выделенной памяти массива), или же память массива является просто коллекцией указателей на объекты, выделенные в другом месте. С точки зрения производительности доступ к встроенным значениям является очевидным преимуществом по сравнению с необходимостью следовать указателю на фактическое значение. Определение isbits обычно означает любой тип Julia с фиксированным детерминированным размером, что означает отсутствие полей указателей, см. ?isbitstype
.
Julia также поддерживает типы объединения, буквально — объединение набора типов. Пользовательские определения типов объединения могут быть чрезвычайно удобны для приложений, стремящихся охватить систему номинальных типов (т. е. явные отношения подтипов) и определить методы или функциональность для этих, иным образом не связанных, наборов типов. Однако задача компилятора заключается в том, чтобы определить способ обработки этих типов объединения. Собственный подход (и действительно то, что работало в Julia до версии 0.7) заключается в том, чтобы просто сделать ячейку, а затем указатель в ячейке на фактическое значение, аналогично ранее упомянутым упакованным значениям. Однако это неудачное решение, поскольку существует множество небольших примитивных типов битов (например, UInt8
, Int32
, Float64
и т. д.), которые легко поместились бы в эту ячейку, не требуя перенаправления для доступа к значению. В версии Julia 0.7 есть два основных способа, оптимизирующих этот подход: поля объединения isbits и массивы объединения isbits.
Структуры объединения isbits
Теперь Julia включает оптимизацию, при которой поля объединения isbits в типах (mutable struct
, struct
и т. д.) будут храниться как встроенные. Это достигается путем определения размера встраивания типа объединения (например, Union{UInt8, Int16}
будет иметь размер 2 байт, что представляет собой размер, необходимый для самого большого типа объединения Int16
), и выделения дополнительного байта метки типа (UInt8
), значение которого указывает на тип фактического значения, хранящегося как встроенное для байтов объединения. Значение байта метки типа является индексом типа фактического значения в порядке типов для типа объединения. Например, значение метки типа 0x02
для поля с типом Union{Nothing, UInt8, Int16}
указывает, что значение Int16
хранится в 16 битах поля в памяти структуры. Значение 0x01
указывает, что значение UInt8
хранится в первых 8 из 16 бит памяти поля. Наконец, значение 0x00
говорит о том, что для этого поля будет возвращено значение nothing
, несмотря на то, что, будучи одинарным типом с единственным экземпляром типа, оно технически имеет размер, равный 0. Байт метки типа для поля объединения типа хранится непосредственно в вычисляемой памяти объединения поля.
Память объединения isbits
Теперь Julia также может хранить значения объединения isbits как встроенные в память в отличие от необходимости использования косвенной ячейки. Оптимизация достигается путем хранения дополнительной памяти меток типов байтов, по одному байту на элемент, наряду с байтами фактических данных. Эта память меток типов выполняет ту же функцию, что и регистр поля типа: его значение указывает на тип фактического хранимого значения объединения. Память меток типов следует непосредственно за обычным пространством данных. Таким образом, формула для доступна к байтам меток типов массива объединения выглядит следующим образом: a->data + a->length * a->elsize
.