Learning Platform
Глоссарий Troubleshooting
Урок 10.04 · 35 мин
Продвинутый
FastLanesSIMDTransposed LayoutBit-PackingData-ParallelFastLanes File FormatDuckDBCWI Amsterdam

FastLanes: SIMD-оптимизированная компрессия

В предыдущем уроке BtrBlocks показал, что каскад из простых кодировок может сжимать лучше, чем одна сложная. Но BtrBlocks не трогает то, как данные раскладываются в памяти перед кодированием. FastLanes (VLDB 2023, Afroozeh & Boncz — CWI Amsterdam) решает ортогональную задачу: как расположить данные так, чтобы любой bit-packing алгоритм автоматически получал SIMD-ускорение — без единой платформенной intrinsic-инструкции.

Проблема: SIMD и bit-packing

Bit-packing — финальная стадия большинства integer encodings. FOR даёт offsets по 4 бита, Delta даёт разности по 3 бита — но эти биты нужно упаковать в байты и распаковать обратно. Скорость распаковки определяет throughput всего pipeline.

Современные CPU имеют SIMD-регистры (SSE = 128 бит, AVX2 = 256 бит, AVX-512 = 512 бит, ARM NEON = 128 бит), которые обрабатывают несколько значений за одну инструкцию. Проблема: написать SIMD-оптимизированный bit-(un)packing нужно отдельно для каждой платформы и для каждой ширины бита (1–64). Это означает тысячи вариантов кода.

Проблема: ручная SIMD-оптимизация bit-packing

Bit-packing: 64 ширины бита × pack + unpack = 128 процедур

Bit-packing: упаковка N-bit значений в непрерывный поток байтов. 64 возможных ширины бита (1–64). Каждая требует своей процедуры pack/unpack.
× платформы
Scalar (baseline)Базовый код без SIMD. Работает на любой платформе, но медленный: ~10 млрд int/sec на bit-unpacking. Компилятор может автовекторизовать, но часто не может — битовые операции плохо ложатся на стандартные SIMD-паттерны.
SSE/AVX2 (x86)Ручные SIMD-intrinsics для x86. 128 процедур переписаны с _mm256_*. Быстрее в 4–8x, но: (1) работает только на x86, (2) AVX-512 — ещё одна полная перезапись, (3) 128+ функций ручного кода — ошибки и поддержка.
ARM NEONТретья реализация — для Apple M1/M2/M3 и серверных Graviton. Свой набор SIMD-intrinsics (vld1q_*, vst1q_*). Снова 128 функций. Apple Silicon — растущая доля серверного рынка.

384+ ручных SIMD-функций. Каждая новая ISA → +128. Не масштабируется.

Итого: 128 × 3+ платформ = 384+ функций ручного SIMD-кода. Каждая новая ISA (RISC-V V, SVE2) добавляет ещё 128. Поддерживать невозможно. FastLanes решает это.

Unified Transposed Layout: порядок 04261537

Ключевая идея FastLanes: вместо написания SIMD-кода под каждую платформу — переупорядочить данные так, чтобы обычный scalar bit-packing цикл компилятор автоматически векторизовал. Трюк — Unified Transposed Layout (UTL).

UTL работает с блоком из 1024 значений. Вместо натурального порядка (0, 1, 2, 3, 4, 5, 6, 7, …) значения переставляются в порядок 04261537 — interleaved по количеству SIMD-лейнов.

Transposed Layout: порядок 04261537 для 8-элементного SIMD
Натуральный порядок (row-major)Стандартный порядок: значения идут последовательно. SIMD-регистр из 8 lanes загружает values[0..7]. Bit-packing между соседними значениями создаёт зависимости: бит value[0] может пересекать границу байта с битами value[1]. Компилятор видит зависимость и не векторизует.
FastLanes transpose
Transposed порядок (04261537)FastLanes переставляет: Lane 0 обрабатывает v[0], v[8], v[16], ... — каждые 8-е значение. Lane 1 обрабатывает v[4], v[12], v[20], ... Порядок лейнов: 0,4,2,6,1,5,3,7. Зачем? Каждый lane работает с НЕЗАВИСИМЫМИ значениями — нет битовых пересечений между лейнами. Компилятор видит 8 независимых потоков → автовекторизация.
Результат

Один scalar for-loop → автовекторизация на SSE/AVX2/AVX-512/NEON

Scalar-код с простым for-loop: for(lane=0; lane<8; lane++) pack(data[lane*stride], ...). Компилятор (Clang/GCC) распознаёт 8 независимых итераций → автоматически генерирует SIMD-инструкции для текущей платформы. SSE → 4 lanes по 32-bit, AVX2 → 8 lanes, AVX-512 → 16. Один и тот же исходный код — разный SIMD на разных CPU.

Почему именно 04261537?

Порядок 04261537 — это bit-reversal permutation для 8 элементов. Он гарантирует, что при любой ширине SIMD-регистра (128, 256, 512 бит) значения в одном lane никогда не пересекают байтовые границы другого lane. FastLanes расширяет это до виртуального 1024-bit SIMD-регистра — 1024 значения в блоке, порядок подобран так, что работает для всех реальных ISA.

Виртуальный 1024-bit SIMD: один код для всех ISA
ISAНабор SIMD-инструкций процессора
РегистрФизическая ширина SIMD-регистра
Lanes (32-bit)Количество 32-bit значений в одном регистре
FastLanesКак FastLanes использует этот ISA
SSE / NEONx86 SSE4.2 или ARM NEON. 128-bit регистры. Минимальный SIMD на современных CPU.
128 бит128 / 32 = 4 значения одновременно
44 lane по 32 бита. Компилятор разворачивает FastLanes loop в 4 параллельных операции.
4 values/instrТот же scalar код — компилятор генерирует SSE/NEON инструкции, обрабатывая 4 значения за такт.
AVX2x86 AVX2. 256-bit регистры. Стандарт на серверных CPU с 2013 (Haswell).
256 бит256 / 32 = 8 значений одновременно
88 lanes по 32 бита.
8 values/instrАвтовекторизация: 8 значений за такт. Примерно 2x ускорение vs SSE.
AVX-512x86 AVX-512. 512-bit регистры. Intel Xeon, Ice Lake+, AMD Zen 4+.
512 бит512 / 32 = 16 значений одновременно
1616 lanes по 32 бита.
16 values/instrАвтовекторизация: 16 значений за такт. ~40% ускорение поверх scalar baseline по замерам FastLanes paper.
Scalar (no SIMD)Без SIMD (или SIMD отключён). Компилятор обрабатывает 1 значение за итерацию.
Нет SIMD-регистра
11 значение за итерацию. Но UTL layout всё равно помогает: данные в cache-friendly порядке.
>100 млрд int/secДаже без SIMD — >100 млрд int/sec на bit-unpacking. UTL layout + компиляторная оптимизация (loop unrolling, pipelining) дают высокий throughput.
NOTE

Порядок 04261537 — упрощение для объяснения на 8 элементах. В полной реализации FastLanes использует 1024-элементные блоки с bit-reversal permutation, адаптированной под целевую виртуальную ширину. Ключевой инвариант: ни один SIMD lane не разделяет битовые boundaries с другим lane — именно это позволяет компилятору безопасно векторизовать.

Data-parallel bit-(un)packing

FastLanes переопределяет bit-packing как data-parallel операцию. Вместо одной функции, которая последовательно пакует значения:

Bit-packing: sequential vs data-parallel (FastLanes)
Sequential bit-packingКлассический подход: обрабатываем значения по одному. value[0] пакуется в биты 0–3, value[1] в биты 4–7, ... Каждый шаг зависит от предыдущего (нужно знать текущую битовую позицию). Зависимость между итерациями = нет автовекторизации.

Зависимость → нет SIMD

Последовательная зависимость: каждая итерация модифицирует buf в позиции, зависящей от предыдущей. Компилятор не может распараллелить.
FastLanes data-parallelFastLanes подход: благодаря UTL layout, 8 (или 16, или 32) значений в разных lanes НИКОГДА не пишут в одни и те же байты. Каждый lane — независимый поток. Компилятор видит: итерации по lanes независимы → автовекторизация.

Независимые lanes → авто-SIMD

Независимые lanes: lane 0 пишет в байты 0–3, lane 1 — в байты 4–7, ... Нет пересечений → нет зависимостей → SIMD.

Полный pipeline декодирования

FastLanes decode pipeline: от файла до значений

Закодированный блок: 1024 × W-bit values (UTL order)

Закодированный блок на диске: 1024 значения по W бит в UTL-порядке. Метаданные: bit_width, base (для FOR), block_count.
1. Чтение блока в память
Загрузка в SIMD-регистрыДанные уже в UTL-порядке на диске — загрузка в SIMD-регистры не требует перестановки. Простой aligned load. На AVX-512: 16 значений за одну vmovdqa64 инструкцию.
2. Data-parallel bit-unpacking
Bit-unpack: W-bit → 32/64-bitКаждый lane независимо распаковывает свои W-bit значения в полные 32-bit (или 64-bit) integers. Операции: shift right, AND mask. Всё через стандартные SIMD operations, сгенерированные компилятором.
3. Inverse transpose (UTL → natural order)
Detranspose: UTL → row-majorОбратная перестановка: из UTL-порядка (04261537...) в натуральный (01234567...). Это SIMD-friendly операция: серия shuffle/permute инструкций. На AVX2: 3–4 vpermps инструкции. Стоимость: <5% от общего decode time.
4. Применение encoding (FOR/Delta)

1024 значений в натуральном порядке. Throughput: > 100 млрд int/sec (scalar)

Финальный шаг: если данные закодированы FOR — прибавляем base к каждому значению (SIMD add). Если Delta — префиксная сумма (SIMD prefix sum). Результат: 1024 восстановленных значения в натуральном порядке.
TIP

Ключевой insight FastLanes: переупорядочивание данных один раз при encode позволяет всем последующим операциям (pack, unpack, FOR, Delta) работать data-parallel без написания SIMD-кода. Компилятор делает всю работу — нужно лишь правильно разложить данные в памяти.

FastLanes File Format v0.1 (VLDB 2025)

В 2025 году те же авторы (Afroozeh & Boncz) опубликовали спецификацию FastLanes File Format — полноценного колоночного формата, построенного вокруг UTL. Два ключевых расширения поверх оригинальной layout-работы:

Expression Encoding

Вместо фиксированного набора кодировок (как в Parquet: PLAIN, RLE_DICTIONARY, DELTA_BINARY_PACKED, …) FastLanes FF определяет кодировки как выражения — комбинации примитивных операций:

Expression Encoding: кодировки как композиции примитивов
Parquet: фиксированные кодировкиParquet определяет кодировки как перечисление (enum): PLAIN=0, RLE_DICTIONARY=2, DELTA_BINARY_PACKED=5, ... Новая кодировка = новый enum ID + обновление спецификации + обновление всех reader/writer. Процесс занимает годы (BYTE_STREAM_SPLIT добавлен в Parquet 2.8, 2021).
FastLanes FF: expression treeFastLanes FF определяет примитивные операции: FOR, Delta, BitPack, Dict, RLE, Const. Кодировка — дерево выражений: FOR(Delta(BitPack(data))). Новая комбинация не требует изменения спецификации — reader уже знает все примитивы. Writer может изобретать новые chains.

Это напрямую поддерживает каскадную идею BtrBlocks: writer может экспериментировать с любыми цепочками кодировок, а reader декодирует их через generic expression evaluator.

Multi-Column Compression (MCC)

Второе расширение: сжатие нескольких колонок совместно, используя корреляции между ними.

Multi-Column Compression: межколоночные корреляции
Column A: timestampКолонка timestamp: 2024-01-15 10:30:00, 2024-01-15 10:30:01, 2024-01-15 10:30:02, ... Монотонно возрастающая — Delta encoding сжимает до 1 бита на значение.
Column B: event_idКолонка event_id: 100001, 100002, 100003, ... Тоже монотонно возрастающая, коррелирует с timestamp (одно событие в секунду).
MCC: B = f(A)
Корреляция: event_id ≈ timestamp_epoch + constMCC обнаруживает линейную корреляцию: event_id = epoch(timestamp) - 1704067800 + 100001. Вместо хранения двух колонок — хранится одна (timestamp) + формула. Column B восстанавливается из A с residual correction (для строк где формула неточна).

Одна колонка + формула + residuals ≈ 50% экономии vs раздельное сжатие

Результат: вместо двух колонок (Delta на каждой) — одна колонка + формула + residual array (часто = all zeros → 1 бит). MCC полезен для log-данных (timestamp коррелирует с sequence_id, offset, row_number).
WARNING

FastLanes File Format (v0.1) — проектная спецификация, не production-ready формат. На момент публикации VLDB 2025 нет широко используемых reader/writer библиотек. DuckDB использует FastLanes-вдохновлённые кодировки внутри своего native формата, но не читает .fastlanes файлы как внешний формат.

Связь с DuckDB

FastLanes и DuckDB — продукты одной исследовательской группы (CWI Amsterdam). Связь конкретная:

FastLanes → DuckDB: как исследование попадает в production
CWI ResearchCentrum Wiskunde & Informatica (Amsterdam). Database Architectures group под руководством Peter Boncz. Публикации: FastLanes (VLDB 2023), ALP (SIGMOD 2024), FSST (VLDB 2020).
GitHub: cwida/FastLanesOpen-source reference implementation. MIT license. Portable C++ — компилируется на x86, ARM, RISC-V. Benchmark suite для воспроизведения результатов paper.
DuckDB LabsКоммерческая компания, основанная Hannes Mühleisen и Mark Raasveldt (оба CWI). DuckDB начался как PhD проект в CWI. FastLanes интегрирован как internal encoding.
DuckDB native formatDuckDB использует FastLanes-inspired bit-packing layout в своём native storage format. Не экспортирует FastLanes File Format — но UTL-принцип применяется к integer columns при storage_info.

Итог

FastLanes — не кодировка и не формат, а layout-принцип: переупорядочить данные один раз так, чтобы все downstream операции получали SIMD-ускорение автоматически. Ключевые числа:

  • >100 млрд integers/sec decode throughput на scalar коде (автовекторизация)
  • ~40% дополнительного ускорения на AVX-512 поверх scalar baseline
  • 0 строк платформенного SIMD-кода — один portable C++ исходник на все ISA
  • 1024 значения в блоке — размер оптимизирован под кэш-линии и register spilling

В следующем уроке мы увидим, как та же группа CWI применила FastLanes к проблеме, которую integer encodings не решают: сжатие чисел с плавающей запятой.

Проверьте понимание

Результат: 0 из 0
Концептуальный
Вопрос 1 из 4. FastLanes Unified Transposed Layout (UTL) переупорядочивает данные по bit-reversal permutation (04261537 для 8 элементов). Зачем?

Закончили урок?

Отметьте его как пройденный, чтобы отслеживать свой прогресс

Войдите чтобы оценить урок

Прогресс модуля
0 из 7