SlideShare a Scribd company logo
Purely practical data
     structures
   Evgeny Lazin (@Lazin),
       Fprog 2012-11
План
1. CIMDB, постановка задачи
2. Решение задачи, использующее могучую
   персистентную структуру данных
3. Еще больше интересных структур данных
CIMDB
● Main memory база данных, созданная в
  компании "Монитор Электрик" (Пятигорск)
CIMDB
1. Common Information Model (CIM).
2. Большой граф объектов, порядка 1KK
элементов.
3. Используется для представления
элементов энергосистемы.
4. Нужно поддерживать целостность
данных.
5. Используется сложными расчетными
задачами.
Немного истории
● Первая реализация - реляционная
  модель.
  ○ Каждая расчетная задача реализует чтение и
    кеширование объектов модели.
  ○ Множество дублирующих друг друга ad-hoc
    реализаций.
  ○ SQL JOIN
    ■ Дай мне все высоковольтные линии,
       связанные с подстанциями, принадлежащими
       определенной генерирующей компании.
  ○ Нет API
Немного истории
● Вторая реализация - client-side cache.
  ○ Решает проблемы:
    ■ изобретения велосипеда.
    ■ общий API.
    ■ могучие join-ы выполняются на клиенте, в
       памяти.
  ○ И создает:
    ■ Проблему целостности данных.
        ●   Инвалидация кэша
     ■ Shared mutable state.
        ●   Shared + mutable = PITA
     ■ Пессимистичные блокировки.
        ●   Блокировки реализованы на уровне БД
     ■ Использует очень много памяти.
Требования
● Распределенность.
● Транзакционность.
  ○ Нужно поддерживать ACID свойства.
  ○ Tradeoff - целостность достаточно поддерживать
    в рамках модели timeline consistency.
  ○ Оптимистичные блокировки.
● Performance.
  ○ Нужно очень быстро читать, на уровне
    предыдущей версии.
  ○ Писать можно не очень быстро.
Странное и необъяснимое
1. Инспектирование изменений и
   управление "миром".
  a. Нужно для технологических задач.
2. Очень быстрое создание снепшотов и
   веток изменений.
  a. Нужно для расчетных задач.
Возможные решения
Чтение данных через TCP/UDP/Pipes
слишком медленно.
  a. Время одного round trip-а - десятки/сотни
     микросекунд.
  b. Требует кэширования на клиенте.
  c. Кэш должен уметь prefetch.
Выход
Поместить клиент и сервер на одну машину,
обмениваться данными через общую
память.

Проблема синхронизации доступа к общей
памяти.
Архитектура.
● Все объекты - неизменяемы. При
  изменении объекта, создается новая
  версия (MVCC).
● У объекта нет номера ревизии, версии
  или метки времени.
● Поиск объектов производится через
  индекс. Для каждой версии существует
  отдельный индекс.
Архитектура
Это позволяет естественным образом
реализовать MVCC и избавиться от
синхронизации при чтении.

Но теперь нам нужен очень эффективный
индекс!
Архитектура
Персистентные структуры данных -
естественный выбор в данной ситуации.
● Позволяют иметь одновременно
  несколько версий одной структуры
  данных, соответствующих разным
  моментам времени.
● Версии не являются полными копиями и
  разделяют часть данных.
Persistency
1. Partially persistent
   ○ Линейная история
   ○ Достаточно для реализации MVCC
2. Fully persistent
   ○ История изменений имеет форму дерева
   ○ Нужно для реализации веток изменений и
     изоляции транзакций
Хэш таблицы
● Очень быстрый поиск по ключу
● Коллизии
  ○ Коллизии возможны даже в том случае, если
    значения hash функции отличаются.
  ○ В нашем случае, у всех объектов есть
    уникальный hash.
● Невозможно сделать неизменяемой
  ○ Можно держать индекс в памяти каждого
    приложения, но это все усложняет.
Бинарные деревья + path copying

Функциональные
версии различных
сбалансированны
х деревьев.
Бинарные деревья
● Медленно
  ○ Скорость поиска по Id должна быть сравнима с
    hash таблицей.
● Глубина пропорциональна log2 N
● Для N = 1KK глубина дерева ~= 20
Префиксные деревья
●   Глубина дерева ограничена длиной ключа
●   Find, Insert, Delete - O(1)
●   Компактное представление в памяти
●   Можно сделать персистентным
Префиксные деревья
● Задача состоит в том, чтобы эффективно
  находить следующий узел.
● Можно использовать фрагмент ключа в
  качестве индекса массива.
Префиксные деревья
struct Node {
    Node[32] array;
}
Префиксные деревья
Для поиска следующего элемента в дереве
мы должны вычислить:

Find(int hash)
    index = hash & 0x1F
    next = this.array[index]
    if next != null:
        next.Find(hash >> 5)
    ...
Префиксные деревья
[00][01001][11001][10001][00011][00001][00000]

                           ...   00000




                 ...    00010    00001   00000




         ...    00011   00010    00001   00000
Префиксные деревья
● Существует множество реализаций
  префиксных деревьев, решающих
  проблему нулевых указателей
● Ideal Hash Trees. Phil Bagwell [2001]
Hash Array Mapped Trie
Реализации:
● Clojure's hash map
  ○ immutable
● Scala - Ctrie
  ○ mutable/concurrent
● Haskell - unordered-containers
● ...
HAMT
Каждый узел хранит:
● Количество дочерних элементов
● Массив дочерних элементов
  ○ Пара ключ - значение
  ○ Указатель на следующий узел
● Bitmap
HAMT
struct Node {
    int count;
    Node[count] array;
    int bitmap;
}


● Узел хранит только неравные null
  элементы
● А также битовую маску для вычисления
  индекса
HAMT
Find(int hash)
    int shift = hash & 0x1F
    int mask = (1 << shift) - 1
    int index = Popcnt(this.bitmap & mask)
    next = this.array[index]
    next.Find(hash >> 5)


Вычисляем значение ключа для поиска
HAMT
Find(int hash)
    int shift = hash & 0x1F
    int mask = (1 << shift) - 1
    int index = Popcnt(this.bitmap & mask)
    next = this.array[index]
    next.Find(hash >> 5)


(1 << shift) вернет слово, в котором будет
установлен 1 бит, индекс которого равен
индексу в несжатом массиве из 32х
элементов
HAMT
Find(int hash)
    int shift = hash & 0x1F
    int mask = (1 << shift) - 1
    int index = Popcnt(this.bitmap & mask)
    next = this.array[index]
    next.Find(hash >> 5)


(1 << shift) - 1 - мы получаем маску, в
которой установлены все биты, чей индекс
меньше индекса искомого элемента
HAMT
Find(int hash)
    int shift = hash & 0x1F
    int mask = (1 << shift) - 1
    int index = Popcnt(this.bitmap & mask)
    next = this.array[index]
    next.Find(hash >> 5)


Popcnt - возвращает количество ненулевых
бит в слове.
HAMT
Find(int hash = 5)
    int shift = 5 & 0x1F
    int mask = (1 << 5) - 1
    int index = Popcnt(100001b & 11111b)
    next = this.array[1]
    next.Find(hash >> 5)

bitmap =
00000000000000000000000000100001b

array = [a, b]
naive = [a, 0, 0, 0, 0, b, ...
HAMT
Можно не сжимать узлы, имеющие
множество дочерних элементов.

Если предполагается наличие дубликатов,
нужен еще один тип узла для разрешения
конфликтов.
HAMT
Реализация на С# (прототип)
● В 2.5 - 3 раза медленнее чем Dictionary
● Расходует сопоставимое количество
  памяти
● Нет коллизий
● Нагружает GC
HAMT
Реализация на Си (production)
● В несколько раз быстрее чем Dictionary
● Использует подсчет ссылок и не
  нагружает GC
● Не использует атомарные инструкции
  (lock xchg и тп) - очень быстро работает в
  одном потоке
Pros
● Простота архитектуры
  ○   Клиент отправляет на сервер транзакции
  ○   В ответ получает указатель на root
  ○   Сообщает о переходе на новую версию
  ○   Очень похоже на VCS
● Сложные вещи реализуются очень просто
  ○ Очень дешево иметь множество слегка
    отличающихся версий одной структуры данных
  ○ Очень легко реализуется изоляция транзакций
● Нет блокировок
  ○ Только координация посредством сообщений
Cons
● Возможны утечки памяти
  ○ Но только если клиент неправильно работает
● Все еще низкая производительность в
  некоторых случаях :)
Ссылки
● Каждый объект может ссылаться на
  другие объекты.
● Физически, эти ссылки реализованы как
  массивы идентификаторов связанных
  объектов.
● Всегда есть обратные ссылки.
Ссылки
● Ходить по ссылкам через индекс -
  достаточно дорого.
● Можно ли хранить внутри объектов не
  только идентификаторы, но и указатели
  на другие объекты?
Ссылки
● Любую связную структуру данных можно
  сделать частично или полностью
  персистентной
● При определенных условиях - за O(1)
  времени и памяти
● "Making Data Structures Persistent" by
  James R. Driscoll, Neil Sarnak, Daniel D.
  Sleator, Robert E. Tarjan
Принцип работы
● Каждый объект может опционально
  хранить один или несколько указателей
  на связанные с ним объекты.
● Каждый объект получает дополнительное
  поле - modification box (m-box)
● m-box может хранить информацию об
  изменениях указателей
● С каждым изменением связан номер
  версии
Пример
immutable list update

                      old value



                      new value




mutable list update

                      old value

       mbox

                      new value
Pros
● Быстрый переход по ссылкам
● Более быстрая проверка ссылочной
  целостности при commit-е
Cons
● Нужно больше памяти
● И процессорного времени при
  обновлениях
● Для чтения нужно знать номер ревизии
● Можно обеспечить только частичную
  персистентность, это означает что:
  ○ Никаких указателей в ветках
  ○ Никаких указателей в транзакциях
Вывод
Данный подход выгодно использовать
только опционально, не для всех типов
объектов и не для всех ссылок.
Вопросы?

More Related Content

What's hot (19)

PPTX
FreeRTOS
quakke
 
PDF
Лекция 3. Оптимизация доступа к памяти (Memory access optimization, cache opt...
Mikhail Kurnosov
 
PDF
Лекция 5. Основы параллельного программирования (Speedup, Amdahl's law, Paral...
Mikhail Kurnosov
 
PPTX
разработка серверов и серверных приложений лекция №3
Eugeniy Tyumentcev
 
PDF
Семинар 12. Параллельное программирование на MPI (часть 5)
Mikhail Kurnosov
 
PDF
Проблемы 64-битного кода на примерах
Tatyanazaxarova
 
PDF
Лекция 2. Оптимизация ветвлений и циклов (Branch prediction and loop optimiz...
Mikhail Kurnosov
 
PPT
04 ос взаимодействие_процессов_1
921519
 
PDF
ПВТ - весна 2015 - Лекция 7. Модель памяти С++. Внеочередное выполнение инстр...
Alexey Paznikov
 
PPTX
Multiprocessor Programming Intro (lecture 1)
Dmitry Tsitelov
 
PDF
Семинар 5. Многопоточное программирование на OpenMP (часть 5)
Mikhail Kurnosov
 
PDF
Кулагин И.И., Пазников А.А., Курносов М.Г. Оптимизация информационных обменов...
Alexey Paznikov
 
PDF
Лекция 8: Многопоточное программирование: Intel Threading Building Blocks
Mikhail Kurnosov
 
PDF
Лекция 5. Основы параллельного программирования (Speedup, Amdahl's law, paral...
Mikhail Kurnosov
 
PDF
Использование Time-Stamp Counter для измерения времени выполнения кода на пр...
Mikhail Kurnosov
 
PDF
C++ CoreHard Autumn 2018. Полезный constexpr - Антон Полухин
corehard_by
 
PPTX
Денис Колодин: Low-latency и soft-realtime на Python
it-people
 
PDF
Семинар 1. Многопоточное программирование на OpenMP (часть 1)
Mikhail Kurnosov
 
PPTX
Паттерны 64-битных ошибок в играх
Andrey Karpov
 
FreeRTOS
quakke
 
Лекция 3. Оптимизация доступа к памяти (Memory access optimization, cache opt...
Mikhail Kurnosov
 
Лекция 5. Основы параллельного программирования (Speedup, Amdahl's law, Paral...
Mikhail Kurnosov
 
разработка серверов и серверных приложений лекция №3
Eugeniy Tyumentcev
 
Семинар 12. Параллельное программирование на MPI (часть 5)
Mikhail Kurnosov
 
Проблемы 64-битного кода на примерах
Tatyanazaxarova
 
Лекция 2. Оптимизация ветвлений и циклов (Branch prediction and loop optimiz...
Mikhail Kurnosov
 
04 ос взаимодействие_процессов_1
921519
 
ПВТ - весна 2015 - Лекция 7. Модель памяти С++. Внеочередное выполнение инстр...
Alexey Paznikov
 
Multiprocessor Programming Intro (lecture 1)
Dmitry Tsitelov
 
Семинар 5. Многопоточное программирование на OpenMP (часть 5)
Mikhail Kurnosov
 
Кулагин И.И., Пазников А.А., Курносов М.Г. Оптимизация информационных обменов...
Alexey Paznikov
 
Лекция 8: Многопоточное программирование: Intel Threading Building Blocks
Mikhail Kurnosov
 
Лекция 5. Основы параллельного программирования (Speedup, Amdahl's law, paral...
Mikhail Kurnosov
 
Использование Time-Stamp Counter для измерения времени выполнения кода на пр...
Mikhail Kurnosov
 
C++ CoreHard Autumn 2018. Полезный constexpr - Антон Полухин
corehard_by
 
Денис Колодин: Low-latency и soft-realtime на Python
it-people
 
Семинар 1. Многопоточное программирование на OpenMP (часть 1)
Mikhail Kurnosov
 
Паттерны 64-битных ошибок в играх
Andrey Karpov
 

Viewers also liked (20)

PPT
Present
lesnoy
 
PPT
(Ppt) mabel self directed learning after session 3
May Mei
 
PDF
Imac all in one
thuongdang1511
 
PPT
Программа развития жилищного строительства на 2012–2016 годы
АО "Самрук-Казына"
 
PPT
Кардиотонические средства
crasgmu
 
PPTX
Intel
Ximena Lopez
 
PPT
Identifying Volume
jmori1
 
PPT
Aarathanai aarathanai
Sampaul Bothirajulu
 
DOC
Publications, Book Chapters, And Selected Patents
azilberstein
 
PPTX
«Профессиональная кухня школьного блоггера, или Для чего нужен блог?»
Татьяна Казанцева
 
PPTX
Pay it forward
Magda_Diego
 
PPT
Kauppi: Mielikuvituksen ja fantasioiden merkitys nuorten mielenterveydessä ja...
Kouluterveyskysely
 
PPTX
Lo lcats
Les Davy
 
PDF
Learn OpenStack from trystack.cn ——Folsom in practice
OpenCity Community
 
PDF
Cartilla de p
Dilmer Hugo Chavez Carrasco
 
PPTX
Our Services increase your business as a Brand name.
Aurelius Corporate Solutions
 
PDF
ファイナンシャル・エクストラネット
KVH Co. Ltd.
 
PDF
Take part in research to combat atherosclerosis
Xplore Health
 
PDF
Celiac disease from gut to brain
Alexander Zolotarjov
 
PPT
Elements, Compounds & Mixtures Spring -2012
jmori1
 
Present
lesnoy
 
(Ppt) mabel self directed learning after session 3
May Mei
 
Imac all in one
thuongdang1511
 
Программа развития жилищного строительства на 2012–2016 годы
АО "Самрук-Казына"
 
Кардиотонические средства
crasgmu
 
Identifying Volume
jmori1
 
Aarathanai aarathanai
Sampaul Bothirajulu
 
Publications, Book Chapters, And Selected Patents
azilberstein
 
«Профессиональная кухня школьного блоггера, или Для чего нужен блог?»
Татьяна Казанцева
 
Pay it forward
Magda_Diego
 
Kauppi: Mielikuvituksen ja fantasioiden merkitys nuorten mielenterveydessä ja...
Kouluterveyskysely
 
Lo lcats
Les Davy
 
Learn OpenStack from trystack.cn ——Folsom in practice
OpenCity Community
 
Our Services increase your business as a Brand name.
Aurelius Corporate Solutions
 
ファイナンシャル・エクストラネット
KVH Co. Ltd.
 
Take part in research to combat atherosclerosis
Xplore Health
 
Celiac disease from gut to brain
Alexander Zolotarjov
 
Elements, Compounds & Mixtures Spring -2012
jmori1
 
Ad

Similar to Purely practical data structures (20)

PDF
Дмитрий Прокопцев "Memory-mapped storage: ещё один подход к сериализации данных"
Yandex
 
PDF
Базы данных. Hash & Cache
Vadim Tsesko
 
PDF
Cравнительный анализ хранилищ данных (Олег Царев, Кирилл Коринский)
Ontico
 
PDF
Олег Царев, Кирилл Коринский Сравнительный анализ хранилищ данных
Siel01
 
PDF
Персистентные структуры данных и архитектура
Vadim Shalts
 
PPT
Redis: возможности, выгоды, примеры использования
Alexey Kachayev
 
PPTX
Cassandra
Ilya Medvedev
 
PDF
16 декабря, DEV {highload} - конференция о Highload веб-разработке, "Строим N...
IT-Portfolio
 
PDF
Велосипедостраительство в NoSQL, строим собственное NoSQL хранилище
Alexandre Kalendarev
 
PDF
Nosql and Mongodb
Eduard Antsupov
 
PDF
Silverspoon2
HighLoad2009
 
PDF
Caching data outside Java Heap and using Shared Memory in Java
Andrei Pangin
 
PPTX
Collections
DmitryTrushkin
 
PDF
Практика Lock-free. RealTime-сервер
Platonov Sergey
 
PPTX
NoSQL - взрыв возможностей
Aleksey Solntsev
 
PDF
BigMemory - работа с сотнями миллионов бизнес-объектов / Дмитрий Хмаладзе (Ag...
Ontico
 
PPTX
Big Data - первые шаги
Anton Gorokhov
 
PPTX
Пишем самый быстрый хеш для кэширования данных
Roman Elizarov
 
PDF
Кеширование данных в БД
Александр Ежов
 
PDF
Software Transactional Memory
Vadim Tsesko
 
Дмитрий Прокопцев "Memory-mapped storage: ещё один подход к сериализации данных"
Yandex
 
Базы данных. Hash & Cache
Vadim Tsesko
 
Cравнительный анализ хранилищ данных (Олег Царев, Кирилл Коринский)
Ontico
 
Олег Царев, Кирилл Коринский Сравнительный анализ хранилищ данных
Siel01
 
Персистентные структуры данных и архитектура
Vadim Shalts
 
Redis: возможности, выгоды, примеры использования
Alexey Kachayev
 
Cassandra
Ilya Medvedev
 
16 декабря, DEV {highload} - конференция о Highload веб-разработке, "Строим N...
IT-Portfolio
 
Велосипедостраительство в NoSQL, строим собственное NoSQL хранилище
Alexandre Kalendarev
 
Nosql and Mongodb
Eduard Antsupov
 
Silverspoon2
HighLoad2009
 
Caching data outside Java Heap and using Shared Memory in Java
Andrei Pangin
 
Collections
DmitryTrushkin
 
Практика Lock-free. RealTime-сервер
Platonov Sergey
 
NoSQL - взрыв возможностей
Aleksey Solntsev
 
BigMemory - работа с сотнями миллионов бизнес-объектов / Дмитрий Хмаладзе (Ag...
Ontico
 
Big Data - первые шаги
Anton Gorokhov
 
Пишем самый быстрый хеш для кэширования данных
Roman Elizarov
 
Кеширование данных в БД
Александр Ежов
 
Software Transactional Memory
Vadim Tsesko
 
Ad

Purely practical data structures

  • 1. Purely practical data structures Evgeny Lazin (@Lazin), Fprog 2012-11
  • 2. План 1. CIMDB, постановка задачи 2. Решение задачи, использующее могучую персистентную структуру данных 3. Еще больше интересных структур данных
  • 3. CIMDB ● Main memory база данных, созданная в компании "Монитор Электрик" (Пятигорск)
  • 4. CIMDB 1. Common Information Model (CIM). 2. Большой граф объектов, порядка 1KK элементов. 3. Используется для представления элементов энергосистемы. 4. Нужно поддерживать целостность данных. 5. Используется сложными расчетными задачами.
  • 5. Немного истории ● Первая реализация - реляционная модель. ○ Каждая расчетная задача реализует чтение и кеширование объектов модели. ○ Множество дублирующих друг друга ad-hoc реализаций. ○ SQL JOIN ■ Дай мне все высоковольтные линии, связанные с подстанциями, принадлежащими определенной генерирующей компании. ○ Нет API
  • 6. Немного истории ● Вторая реализация - client-side cache. ○ Решает проблемы: ■ изобретения велосипеда. ■ общий API. ■ могучие join-ы выполняются на клиенте, в памяти. ○ И создает: ■ Проблему целостности данных. ● Инвалидация кэша ■ Shared mutable state. ● Shared + mutable = PITA ■ Пессимистичные блокировки. ● Блокировки реализованы на уровне БД ■ Использует очень много памяти.
  • 7. Требования ● Распределенность. ● Транзакционность. ○ Нужно поддерживать ACID свойства. ○ Tradeoff - целостность достаточно поддерживать в рамках модели timeline consistency. ○ Оптимистичные блокировки. ● Performance. ○ Нужно очень быстро читать, на уровне предыдущей версии. ○ Писать можно не очень быстро.
  • 8. Странное и необъяснимое 1. Инспектирование изменений и управление "миром". a. Нужно для технологических задач. 2. Очень быстрое создание снепшотов и веток изменений. a. Нужно для расчетных задач.
  • 9. Возможные решения Чтение данных через TCP/UDP/Pipes слишком медленно. a. Время одного round trip-а - десятки/сотни микросекунд. b. Требует кэширования на клиенте. c. Кэш должен уметь prefetch.
  • 10. Выход Поместить клиент и сервер на одну машину, обмениваться данными через общую память. Проблема синхронизации доступа к общей памяти.
  • 11. Архитектура. ● Все объекты - неизменяемы. При изменении объекта, создается новая версия (MVCC). ● У объекта нет номера ревизии, версии или метки времени. ● Поиск объектов производится через индекс. Для каждой версии существует отдельный индекс.
  • 12. Архитектура Это позволяет естественным образом реализовать MVCC и избавиться от синхронизации при чтении. Но теперь нам нужен очень эффективный индекс!
  • 13. Архитектура Персистентные структуры данных - естественный выбор в данной ситуации. ● Позволяют иметь одновременно несколько версий одной структуры данных, соответствующих разным моментам времени. ● Версии не являются полными копиями и разделяют часть данных.
  • 14. Persistency 1. Partially persistent ○ Линейная история ○ Достаточно для реализации MVCC 2. Fully persistent ○ История изменений имеет форму дерева ○ Нужно для реализации веток изменений и изоляции транзакций
  • 15. Хэш таблицы ● Очень быстрый поиск по ключу ● Коллизии ○ Коллизии возможны даже в том случае, если значения hash функции отличаются. ○ В нашем случае, у всех объектов есть уникальный hash. ● Невозможно сделать неизменяемой ○ Можно держать индекс в памяти каждого приложения, но это все усложняет.
  • 16. Бинарные деревья + path copying Функциональные версии различных сбалансированны х деревьев.
  • 17. Бинарные деревья ● Медленно ○ Скорость поиска по Id должна быть сравнима с hash таблицей. ● Глубина пропорциональна log2 N ● Для N = 1KK глубина дерева ~= 20
  • 18. Префиксные деревья ● Глубина дерева ограничена длиной ключа ● Find, Insert, Delete - O(1) ● Компактное представление в памяти ● Можно сделать персистентным
  • 19. Префиксные деревья ● Задача состоит в том, чтобы эффективно находить следующий узел. ● Можно использовать фрагмент ключа в качестве индекса массива.
  • 21. Префиксные деревья Для поиска следующего элемента в дереве мы должны вычислить: Find(int hash) index = hash & 0x1F next = this.array[index] if next != null: next.Find(hash >> 5) ...
  • 22. Префиксные деревья [00][01001][11001][10001][00011][00001][00000] ... 00000 ... 00010 00001 00000 ... 00011 00010 00001 00000
  • 23. Префиксные деревья ● Существует множество реализаций префиксных деревьев, решающих проблему нулевых указателей ● Ideal Hash Trees. Phil Bagwell [2001]
  • 24. Hash Array Mapped Trie Реализации: ● Clojure's hash map ○ immutable ● Scala - Ctrie ○ mutable/concurrent ● Haskell - unordered-containers ● ...
  • 25. HAMT Каждый узел хранит: ● Количество дочерних элементов ● Массив дочерних элементов ○ Пара ключ - значение ○ Указатель на следующий узел ● Bitmap
  • 26. HAMT struct Node { int count; Node[count] array; int bitmap; } ● Узел хранит только неравные null элементы ● А также битовую маску для вычисления индекса
  • 27. HAMT Find(int hash) int shift = hash & 0x1F int mask = (1 << shift) - 1 int index = Popcnt(this.bitmap & mask) next = this.array[index] next.Find(hash >> 5) Вычисляем значение ключа для поиска
  • 28. HAMT Find(int hash) int shift = hash & 0x1F int mask = (1 << shift) - 1 int index = Popcnt(this.bitmap & mask) next = this.array[index] next.Find(hash >> 5) (1 << shift) вернет слово, в котором будет установлен 1 бит, индекс которого равен индексу в несжатом массиве из 32х элементов
  • 29. HAMT Find(int hash) int shift = hash & 0x1F int mask = (1 << shift) - 1 int index = Popcnt(this.bitmap & mask) next = this.array[index] next.Find(hash >> 5) (1 << shift) - 1 - мы получаем маску, в которой установлены все биты, чей индекс меньше индекса искомого элемента
  • 30. HAMT Find(int hash) int shift = hash & 0x1F int mask = (1 << shift) - 1 int index = Popcnt(this.bitmap & mask) next = this.array[index] next.Find(hash >> 5) Popcnt - возвращает количество ненулевых бит в слове.
  • 31. HAMT Find(int hash = 5) int shift = 5 & 0x1F int mask = (1 << 5) - 1 int index = Popcnt(100001b & 11111b) next = this.array[1] next.Find(hash >> 5) bitmap = 00000000000000000000000000100001b array = [a, b] naive = [a, 0, 0, 0, 0, b, ...
  • 32. HAMT Можно не сжимать узлы, имеющие множество дочерних элементов. Если предполагается наличие дубликатов, нужен еще один тип узла для разрешения конфликтов.
  • 33. HAMT Реализация на С# (прототип) ● В 2.5 - 3 раза медленнее чем Dictionary ● Расходует сопоставимое количество памяти ● Нет коллизий ● Нагружает GC
  • 34. HAMT Реализация на Си (production) ● В несколько раз быстрее чем Dictionary ● Использует подсчет ссылок и не нагружает GC ● Не использует атомарные инструкции (lock xchg и тп) - очень быстро работает в одном потоке
  • 35. Pros ● Простота архитектуры ○ Клиент отправляет на сервер транзакции ○ В ответ получает указатель на root ○ Сообщает о переходе на новую версию ○ Очень похоже на VCS ● Сложные вещи реализуются очень просто ○ Очень дешево иметь множество слегка отличающихся версий одной структуры данных ○ Очень легко реализуется изоляция транзакций ● Нет блокировок ○ Только координация посредством сообщений
  • 36. Cons ● Возможны утечки памяти ○ Но только если клиент неправильно работает ● Все еще низкая производительность в некоторых случаях :)
  • 37. Ссылки ● Каждый объект может ссылаться на другие объекты. ● Физически, эти ссылки реализованы как массивы идентификаторов связанных объектов. ● Всегда есть обратные ссылки.
  • 38. Ссылки ● Ходить по ссылкам через индекс - достаточно дорого. ● Можно ли хранить внутри объектов не только идентификаторы, но и указатели на другие объекты?
  • 39. Ссылки ● Любую связную структуру данных можно сделать частично или полностью персистентной ● При определенных условиях - за O(1) времени и памяти ● "Making Data Structures Persistent" by James R. Driscoll, Neil Sarnak, Daniel D. Sleator, Robert E. Tarjan
  • 40. Принцип работы ● Каждый объект может опционально хранить один или несколько указателей на связанные с ним объекты. ● Каждый объект получает дополнительное поле - modification box (m-box) ● m-box может хранить информацию об изменениях указателей ● С каждым изменением связан номер версии
  • 41. Пример immutable list update old value new value mutable list update old value mbox new value
  • 42. Pros ● Быстрый переход по ссылкам ● Более быстрая проверка ссылочной целостности при commit-е
  • 43. Cons ● Нужно больше памяти ● И процессорного времени при обновлениях ● Для чтения нужно знать номер ревизии ● Можно обеспечить только частичную персистентность, это означает что: ○ Никаких указателей в ветках ○ Никаких указателей в транзакциях
  • 44. Вывод Данный подход выгодно использовать только опционально, не для всех типов объектов и не для всех ссылок.