SMI — Small Integer в V8

Способ V8 хранить маленькие целые числа внутри указателя без аллокации объекта. Через pointer tagging: младший бит указателя помечает, число это или ссылка.

Механика

  • Указатель в V8 = слово CPU. Младший бит используется как tag.
  • bit = 1 → перед нами данные (SMI, целое число прямо в указателе).
  • bit = 0 → перед нами ссылка на объект в куче.
  • Это позволяет не аллоцировать объект Number для каждой циферки.

«Чтобы не выделять новый числовой объект каждый раз, V8 с помощью техники тегирования указателей позволяет хранить маленькие числа.»

Диапазон SMI зависит от архитектуры

Архитектура SMI диапазон
32-bit V8 31 бит знакового целого
64-bit V8 (без pointer compression) 32 бита
64-bit с pointer compression снова 31 бит

Цитата: «SMI в зависимости от обстоятельств будет совершенно разным. Если архитектура 32-битная, то SMI это 31 бит, выделяемый на знаковое целое.»

Pointer Compression

V8 сжимает указатели на 64-битке до 32 бит (база + смещение). Экономия памяти 2x, но и SMI снова на 31 бит.

Подводные камни

  • Число вышло за SMI → V8 аллоцирует HeapNumber (double в куче) → касание GC и потеря скорости.
  • Array.smi (массив маленьких целых) ≠ Array.double (с плавающей точкой) — разные внутренние storage.
  • arr[0] = 1.5 после ряда целых → массив переходит в double-режим (deoptimization).
  • В одном бенчмарке (Node) массив double-ов оказался быстрее массива SMI из-за особенностей аллокации через push — но в чистом V8 и Chrome SMI быстрее в 2 раза.

Источники

См. также