SlideShare a Scribd company logo
Python dictionary
прошлое, настоящее, будущее
Dmitry Alimov
Senior Software Engineer
Zodiac Interactive
2016
SPb Python Interest Group
Словарь в Python
>>> d = {} # то же самое, что d = dict()
>>> d['a'] = 123
>>> d['b'] = 345
>>> d['c'] = 678
>>> d
{'a': 123, 'c': 678, 'b': 345}
>>> d['b']
345
>>> del d['c']
>>> d
{'a': 123, 'b': 345}
Ключами словаря могут быть значения только hashable типов
>>> d[list()] = 1
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'
>>> d[set()] = 2
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'set'
>>> d[dict()] = 3
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'dict'
Все иммутабельные built-in’ы – hashable
import random
class A(object):
def __init__(self, index):
self.index = index
def __eq__(self, other):
return True
def __hash__(self):
return random.randint(0, 3)
def __repr__(self):
return 'A%d' % self.index
d = {A(0): 0, A(1): 1, A(2): 2}
print('keys: %s' % d.keys())
print('values: %s' % d.values())
for k in d:
print('%s = %s' % (k, d.get(k, 'not found')))
Random hash – плохая идея
Запуск 1
keys: [A1, A2, A0]
values: [1, 2, 0]
A1 = 1
A2 = not found
A0 = 0
Запуск 2
keys: [A1, A0]
values: [2, 0]
A1 = not found
A0 = not found
Прошлое
Ячейка в хэш-таблице может иметь три состояния:
1) Неиспользованная
2) Активная
3) Пустая
typedef struct {
Py_ssize_t me_hash;
PyObject *me_key;
PyObject *me_value;
} PyDictEntry;
- Хэш-таблица
- Разрешения коллизий методом открытой адресации
- Начальный размер = 8
- Коэффициент заполнения = 2/3
- Коэффициент роста = 4 или 2 (зависит от числа используемых ячеек)
- “/Include/dictobject.h”, “/Objects/dictobject.c”, “/Objects/dictnotes.txt”
Словарь в CPython >2.1
ma_fill – сумма «активных» и «пустых» ячеек
ma_used – число «активных» ячеек
ma_mask – маска, равная PyDict_MINSIZE - 1
ma_lookup – функция поиска (по умолчанию lookdict_string)
#define PyDict_MINSIZE 8
typedef struct _dictobject PyDictObject;
struct _dictobject {
PyObject_HEAD
Py_ssize_t ma_fill;
Py_ssize_t ma_used;
Py_ssize_t ma_mask;
PyDictEntry *ma_table;
PyDictEntry *(*ma_lookup)(PyDictObject *mp, PyObject *key,
long hash);
PyDictEntry ma_smalltable[PyDict_MINSIZE];
};
Нужны хорошие хэш-функции
>>> map(hash, [0, 1, 2, 3, 4])
[0, 1, 2, 3, 4]
>>> map(hash, ['abca', 'abcb', 'abcc', 'abcd', 'abce'])
[1540938117, 1540938118, 1540938119, 1540938112, 1540938113]
Модифицированная хэш-функция FNV (Fowler–Noll–Vo) для строк
Ключ “-R” интерпретатора для псевдо-случайной соли (строки, bytes и
объекты datetime)
>>> map(hash, ['abca', 'abcb', 'abcc', 'abcd', 'abce'])
[-218138032, -218138029, -218138030, -218138027, -218138028]
Хэш-функции
Разрешение коллизий
Коллизия – ситуация, при которой разные входные значения (ключи) имеют
одинаковое значение хэша.
Процедура выбора подходящей ячейки для вставки элемента в хэш-таблицу
называется пробирование, а рассматриваемая ячейка-кандидат – проба.
В CPython – используется пробирование с псевдослучайным шагом
PERTURB_SHIFT = 5
perturb = hash(key)
while True:
j = (5 * j) + 1 + perturb
perturb >>= PERTURB_SHIFT
index = j % 2**i
См. “/Objects/dictobject.c”
В CPython <2.2 использовался расчёт индекса основанный на многочленах
>>> PyDict_MINSIZE = 8
>>> key = 123
>>> hash(key) % PyDict_MINSIZE
>>> 3
Расчѐт индекса
>>> mask = PyDict_MINSIZE - 1
>>> hash(key) & mask
>>> 3
Вместо деления по модулю используется логическая операция «И» и маска
Так получаются младшие биты хэша:
2 ** i = PyDict_MINSIZE, отсюда i = 3, т.е. достаточно трёх младших бит
hash(123) = 123 = 0b1111011
mask = PyDict_MINSIZE - 1 = 8 - 1 = 7 = 0b111
index = hash(123) & mask = 0b1111011 & 0b111 = 0b011 = 3
mask = PyDict_MINSIZE - 1
index = hash(123) & mask
Целые
Строки
mask = PyDict_MINSIZE - 1
index = hash(123) & mask
Словарь в CPython >2.1
Инициализация словаря
Добавление элемента
PyDict_SetItem()
PyDict_New() ma_used = 0
ma_fill = 0
ma_mask = PyDict_MINSIZE – 1
ma_table = ma_smalltable
ma_lookup = lookdict_string
insertdict()
ma_used += 1
ma_fill += 1
dictresize() если ma_fill >= 2/3 * size
Удаление элемента
PyDict_DelItem() ma_used -= 1
Добавление элемента
Добавление элемента
Добавление элемента
Добавление элемента
Добавление элемента
perturb = -1297030748
# i = (i * 5) + 1 + perturb
i = (4 * 5) + 1 + (-1297030748) = -1297030727
index = -1297030727 & 7 = 1
hash('!!!') = -1297030748
i = -1297030748 & 7 = 4
# perturb = perturb >> PERTURB_SHIFT
perturb = -1297030748 >> 5 = -40532211
# i = (i * 5) + 1 + perturb
i = (-1297030727 * 5) + 1 + (-40532211) = -6525685845
index = -6525685845 & 7 = 3
>>> d
{'python': 2, 'article': 4, '!!!': 5, 'dict': 3, 'a key': 1}
>>> d.__sizeof__()
248
Добавление элемента
Измерение размера хэш-таблицы
>>> d
{'!!!': 5, 'python': 2, 'dict': 3, 'a key': 1, 'article': 4, ';)': 6}
>>> d.__sizeof__()
1016
Измерение размера хэш-таблицы
/* Find the smallest table size > minused. */
for (newsize = 8;
newsize <= minused && newsize > 0;
newsize <<= 1)
;
...
}
dictresize(PyDictObject *mp, Py_ssize_t minused) {
...
PyDict_SetItem(...) {
...
dictresize(mp, (mp->ma_used > 50000 ? 2 : 4) * mp->ma_used);
...
}
В нашем примере:
ma_fill = 6 > (8 * 2 / 3)
ma_used = 6
отсюда minused = 4 * 6 = 24, следовательно newsize = 32
Порядок добавления ключей
>>> d1 = {'one': 1, 'two': 2, 'three': 3, 'four': 4, 'five': 5}
>>> d2 = {'three': 3, 'two': 2, 'five': 5, 'four': 4, 'one': 1}
>>> d1 == d2
True
>>> d1.keys()
['four', 'three', 'five', 'two', 'one']
>>> d2.keys()
['four', 'one', 'five', 'three', 'two']
Индексы добавляемых в словарь элементов зависят от находящихся в нём элементов
>>> 7.0 == 7 == (7+0j)
True
>>> d = {}
>>> d[7.0] = 'float'
>>> d
{7.0: 'float'}
>>> d[7] = 'int'
>>> d
{7.0: 'int'}
>>> d[7+0j] = 'complex'
>>> d
{7.0: 'complex'}
>>> type(d.keys()[0])
<type 'float'>
int, float, complex
>>> hash(7)
7
>>> hash(7.0)
7
>>> hash(7+0j)
7
>>> d = {'a': 1}
>>> for i in d:
... d['new item'] = 123
...
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
RuntimeError: dictionary changed size during iteration
Добавление элемента
во время итерации
Удаление элемента
dummy = PyString_FromString("<dummy key>"));
Интересный случай
Интересный случай
ma_fill = 6 > (8 * 2 / 3) dictresize()
Интересный случай
ma_fill = 6 > (8 * 2 / 3)
ma_used = 1
отсюда minused = 4 * 1 = 4, следовательно newsize = 8
Кэш
PyDictEntry ma_smalltable[8];
На x86 с линейкой кэша в 64 байта – в одну линейку входит:
64 / (4 * 3) = 5.33 элементов PyDictEntry
typedef struct {
Py_ssize_t me_hash;
PyObject *me_key;
PyObject *me_value;
} PyDictEntry;
Оптимизация локальности и коллизии
См. “/Objects/dictnotes.txt”
Источник Время доступа
Кэш L1 1 нс
Кэш L2 4 нс
RAM 100 нс
Открытая адресация vs метод цепочек
Хотя тут линейное пробирование, а не псевдослучайное как в CPython.
OrderedDict
from collections import OrderedDict
- Внутренний словарь
- Кольцевой (циклический, замкнутый) двусвязный список
- “/Lib/collections/__init__.py”
Настоящее
Словарь в CPython 3.5
- PEP 412 - Key-Sharing Dictionary
- Может быть в одной из двух форм: комбинированная таблица и сплит-таблица
- Начальный размер = 4 (сплит-таблица) или 8 (комбинированная таблица)
- Максимальное заполнение = (2*n+1)/3
- Коэффициент роста = used*2 + capacity/2
- “/Objects/dict-common.h”, “/Include/dictobject.h”, “/Objects/dictobject.c”,
“/Objects/dictnotes.txt”
typedef struct {
Py_hash_t me_hash;
PyObject *me_key;
PyObject *me_value; /* only meaningful for combined tables */
} PyDictKeyEntry;
struct _dictkeysobject {
Py_ssize_t dk_refcnt;
Py_ssize_t dk_size;
dict_lookup_func dk_lookup;
Py_ssize_t dk_usable;
PyDictKeyEntry dk_entries[1];
};
typedef struct {
PyObject_HEAD
Py_ssize_t ma_used;
PyDictKeysObject *ma_keys;
PyObject **ma_values;
} PyDictObject;
Комбинированная таблица vs cплит-таблица
Комбинированная таблица
- Для хранения всех явно созданных словарей (dict() и {})
- ma_values = NULL
- Никогда не может стать сплит-таблицей
Сплит-таблица
- Для хранения __dict__ объектов
- Ключи – только строки
- Отдельная таблица для значений (ma_values)
- После изменения размера превращается в комбинированную (но если
изменение размера происходит из-за setattr и существует только один
экземпляр класса, происходит ре-сплит)
- Для поиска используется lookdict_split
Словарь в CPython 3.5
Новое состояние ячейки в хэш-таблице для сплит-таблицы:
1) Неиспользованная
2) Активная
3) Пустая
4) Ожидающая (me_key != NULL, me_key != dummy и me_value == NULL)
typedef struct {
Py_hash_t me_hash;
PyObject *me_key;
PyObject *me_value; /* only meaningful for combined tables */
} PyDictKeyEntry;
Сплит-таблица
Начальный размер = 4
Максимальное заполнение = (2*n+1)/3 = (2*4+1)/3 = 3,
то есть изначально ma_keys->dk_usable = 3
Сплит-таблица
class A():
def __init__(self):
self.a = 1
self.b = 2
self.c = 3
a = A()
print(a.__dict__.__sizeof__()) # 72
setattr(a, 'd', 4) # респлит
print(a.__dict__.__sizeof__()) # 168
print({}.__sizeof__()) # 264
Начальный размер = 4
Максимальное заполнение = (2*n+1)/3 = (2*4+1)/3 = 3
Коэффициент роста = used*2 + capacity/2 = 3*2 + 4/2 = 8, отсюда
minused = 8, следовательно newsize = 16 (см. dictresize)
class A():
def __init__(self):
self.a = 1
self.b = 2
self.c = 3
a = A()
print(a.__dict__.__sizeof__()) # 72
b = A()
setattr(a, 'd', 4) # респлита нет из-за b
print(a.__dict__.__sizeof__()) # 456
Сплит-таблица
Сплит-таблица превратилась в комбинированную таблицу
Ключевые отличия от CPython 2.x:
- Таблица может быть разделена на Ключи и Значения
- Добавлено новое состояние ячейки
- Больше нет ma_smalltable в структуре
- Обычные словари стали немного больше
- Выигрыш в памяти до 60% для программ, использующих много ООП (в
соответствии с https://github.com/python/cpython/blob/3.5/Objects/dictnotes.txt)
Всё ещё случаются баги типа: Unbounded memory growth resizing split-table
dicts (https://bugs.python.org/issue28147)
Резюме
Хэш-функции в CPython 3.5
SipHash для строк (>= CPython 3.4)
- Стойкий к hash-flooding DoS атакам
- Успешно используется во многих других языках
Немного изменённые хэш-функции для float, int
PEP 456 – Secure and interchangeable hash algorithm
hash(float("+inf")) == 314159,
hash(float("-inf")) == -314159, а было -271828
OrderedDict в CPython 3.5
- Двусвязный список
- Хэш таблица od_fast_nodes c зеркальным отображением словаря od_dict
- “/Include/odictobject.h”, “/Objects/odictobject.c”
Альтернативные версии
Словарь в PyPy
- Начиная с PyPy 2.5.0 по умолчанию – ordereddict
- Начальный размер 16
- Коэффициент заполнения до 2/3
- Коэффициент роста 4 (до 30000 элементов) или 2
- При удалении множества элементов выполняется уплотнение
- “/rpython/rtyper/lltypesystem/rordereddict.py”
struct dicttable {
int num_live_items;
int num_ever_used_items;
int resize_counter;
variable_int *indexes; // byte, short, int, long
dictentry *entries;
...
}
struct dictentry {
PyObject *key;
PyObject *value;
long hash;
bool valid;
}
Словарь в PyPy
struct dicttable {
variable_int *indexes;
dictentry *entries;
...
}
FREE = 0
DELETED = 1
VALID_OFFSET = 2
PyDictionary в Jython
- Построен на ConcurrentHashMap
- Разрешения коллизий методом цепочек (separate chaining)
- Начальный размер = 16, коэффициент заполнения = 0.75, коэффициент роста = 2
- Сегменты и потокобезопасность
PythonDictionary в IronPython
- Построен на Dictionary (.NET)
- Разрешения коллизий методом цепочек
- Начальный размер = 0, коэффициент заполнения = 1.0
- Рехэшинг в случае если число коллизий >= 100
- Коэффициент роста = 2 (новый размер равен ближайшему большему простому числу)
из ряда primes = {3, 7, 11, 17, 23, 29, 37, 47, 59, 71, 89, 107,… , 4999559, 5999471, 7199369}
Будущее
Raymond Hettinger доволен
Словарь в CPython 3.6
typedef struct {
Py_hash_t me_hash;
PyObject *me_key;
PyObject *me_value; /* only meaningful for combined tables */
} PyDictKeyEntry;
typedef struct {
PyObject_HEAD
Py_ssize_t ma_used; /* number of items in the dictionary */
uint64_t ma_version_tag; /* unique, changes when dict modified */
PyDictKeysObject *ma_keys;
PyObject **ma_values;
} PyDictObject;
- Добавили версию ma_version_tag (PEP 509 – Add a private version to dict)
- Начальный размер = 8 (для сплит-таблицы тоже)
- Максимальное заполнение = (2*n)/3
- Добавил INADA Naoki в https://bugs.python.org/issue27350
Состояния ячеек в хэш-таблице:
1) Неиспользованная (index == DKIX_EMPTY == -1)
2) Активная (index >= 0 , me_key != NULL и me_value != NULL)
3) Пустая (index == DKIX_DUMMY == -2, только для комбинированных таблиц)
4) Ожидающая (index >= 0 , me_key != NULL и me_value == NULL, только для сплит-таблиц)
Словарь в CPython 3.6
- Добавили dk_nentries и dk_indices
struct _dictkeysobject {
Py_ssize_t dk_refcnt;
Py_ssize_t dk_size; /* Size of the hash table (dk_indices) */
dict_lookup_func dk_lookup; /* Function to lookup in dk_indices */
Py_ssize_t dk_usable; /* Number of usable entries in dk_entries */
Py_ssize_t dk_nentries; /* Number of used entries in dk_entries */
union {
int8_t as_1[8];
int16_t as_2[4];
int32_t as_4[2];
#if SIZEOF_VOID_P > 4
int64_t as_8[1];
#endif
} dk_indices;
PyDictKeyEntry dk_entries[dk_usable]; /* using DK_ENTRIES macro */
};
Словарь в CPython 3.6
(Комбинированная таблица)
Ключевые отличия от CPython 3.5:
- Добавили dk_indices с типом, зависящим от размера
- Добавили версию ma_version_tag (PEP 509)
- Изменили начальный размер для сплит-таблицы на 8
- Изменили максимальное заполнение на (2*n)/3
- При удалении из сплит-таблицы она становится комбинированной
- Решена проблема сохранения порядка **kwargs (PEP 468)
- Решена проблема сохранения порядка атрибутов класса (PEP 520)
- Использование памяти на 20-25% меньше по сравнению с CPython 3.5
(https://docs.python.org/3.6/whatsnew/3.6.html#other-language-changes)
Резюме
Ссылки
1. Реализация словаря в Python 2.7 https://habrahabr.ru/post/247843/
2. Python hash calculation algorithms http://delimitry.blogspot.com/2014/07/python-hash-calculation-algorithms.html
3. PEP 412 - Key-Sharing Dictionary https://www.python.org/dev/peps/pep-0412/
4. PEP 456 - Secure and interchangeable hash algorithm https://www.python.org/dev/peps/pep-0456/
5. Mirror of the CPython repository https://github.com/python/cpython/
6. Faster, more memory efficient and more ordered dictionaries on PyPy https://morepypy.blogspot.ru/2015/01/faster-
more-memory-efficient-and-more.html
7. PyDictionary (Jython API documentation) http://www.jython.org/javadoc/org/python/core/PyDictionary.html
8. Jython repository https://bitbucket.org/jython/jython
9. Теория и практика Java: Построение лучшей HashMap http://www.ibm.com/developerworks/ru/library/j-jtp08223/
10. Back to basics: Dictionary part 2, .NET implementation https://blog.markvincze.com/back-to-basics-dictionary-part-2-
net-implementation/
11. http://referencesource.microsoft.com/mscorlib/system/collections/generic/dictionary.cs.html
12. https://github.com/IronLanguages/main/blob/ipy-2.7-maint/Languages/IronPython/IronPython/
13. https://bitbucket.org/pypy/pypy/
14. https://twitter.com/raymondh
15. PEP 509 - Add a private version to dict https://www.python.org/dev/peps/pep-0509/
16. Compact and ordered dict http://bugs.python.org/issue27350
17. What’s New In Python 3.6 https://docs.python.org/3.6/whatsnew/3.6.html
18. PEP 468 - Preserving the order of **kwargs in a function https://www.python.org/dev/peps/pep-0468/
19. PEP 520 - Preserving Class Attribute Definition Order https://www.python.org/dev/peps/pep-0520/
Картинки с сайтов:
http://www.rcreptiles.com/blog/index.php/2008/06/28/read_the_operating_manual_first
http://kiwigamer450.deviantart.com/art/Back-to-The-Past-Logo-567858767
http://beyondplm.com/wp-content/uploads/2014/04/time-paradox-past-future-present.jpg
http://itband.ru/wp-content/uploads/2014/10/Future.jpg
https://en.wikipedia.org/wiki/Hash_table
Q & A
@delimitry
spbpython.guru
SPb Python Interest Group
Дополнительные слайды
Разрешение коллизий методом цепочек
Разрешение коллизий методом открытой адресации
(псевдослучайное пробирование)

More Related Content

Similar to Python dict: прошлое, настоящее, будущее

Красота и изящность стандартной библиотеки Python
Красота и изящность стандартной библиотеки PythonКрасота и изящность стандартной библиотеки Python
Красота и изящность стандартной библиотеки Python
Python Meetup
 
[JAM 1.1] Clean Code (Paul Malikov)
[JAM 1.1] Clean Code (Paul Malikov)[JAM 1.1] Clean Code (Paul Malikov)
[JAM 1.1] Clean Code (Paul Malikov)
Evgeny Kaziak
 
Лекция #5. Введение в язык программирования Python 3
Лекция #5. Введение в язык программирования Python 3Лекция #5. Введение в язык программирования Python 3
Лекция #5. Введение в язык программирования Python 3
Яковенко Кирилл
 
Производительность в Django
Производительность в DjangoПроизводительность в Django
Производительность в Django
MoscowDjango
 
Лекция 8. Intel Threading Building Blocks
Лекция 8. Intel Threading Building BlocksЛекция 8. Intel Threading Building Blocks
Лекция 8. Intel Threading Building Blocks
Mikhail Kurnosov
 
DSLs в Perl
DSLs в PerlDSLs в Perl
DSLs в Perl
Ruslan Zakirov
 
C++ refelection and cats
C++ refelection and catsC++ refelection and cats
C++ refelection and cats
corehard_by
 
Основы языка R
Основы языка RОсновы языка R
Основы языка R
Sergey Mastitsky
 
Лекция 5. Встроенные коллекции и модуль collections.
Лекция 5. Встроенные коллекции и модуль collections.Лекция 5. Встроенные коллекции и модуль collections.
Лекция 5. Встроенные коллекции и модуль collections.
Roman Brovko
 
Лекция 8: Многопоточное программирование: Intel Threading Building Blocks
Лекция 8: Многопоточное программирование: Intel Threading Building BlocksЛекция 8: Многопоточное программирование: Intel Threading Building Blocks
Лекция 8: Многопоточное программирование: Intel Threading Building BlocksMikhail Kurnosov
 
8 встреча — Язык программирования Python (В. Ананьев)
8 встреча — Язык программирования Python (В. Ананьев)8 встреча — Язык программирования Python (В. Ананьев)
8 встреча — Язык программирования Python (В. Ананьев)
Smolensk Computer Science Club
 
Charming python sc2-8
Charming python sc2-8Charming python sc2-8
Charming python sc2-8
Vladislav Ananev
 
PHP Tricks
PHP TricksPHP Tricks
PHP TricksBlackFan
 
Лекция 2. Всё, что вы хотели знать о функциях в Python.
Лекция 2. Всё, что вы хотели знать о функциях в Python.Лекция 2. Всё, что вы хотели знать о функциях в Python.
Лекция 2. Всё, что вы хотели знать о функциях в Python.
Roman Brovko
 
Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014
Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014
Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014
Python Meetup
 
Объектно-ориентированное программирование. Лекции 9 и 10
Объектно-ориентированное программирование. Лекции 9 и 10Объектно-ориентированное программирование. Лекции 9 и 10
Объектно-ориентированное программирование. Лекции 9 и 10
Dima Dzuba
 
ПВТ - осень 2014 - Лекция 4 - Стандарт POSIX Threads. Реентерабельность. Сигн...
ПВТ - осень 2014 - Лекция 4 - Стандарт POSIX Threads. Реентерабельность. Сигн...ПВТ - осень 2014 - Лекция 4 - Стандарт POSIX Threads. Реентерабельность. Сигн...
ПВТ - осень 2014 - Лекция 4 - Стандарт POSIX Threads. Реентерабельность. Сигн...
Alexey Paznikov
 
Python
PythonPython
Pythonpelid
 

Similar to Python dict: прошлое, настоящее, будущее (20)

Красота и изящность стандартной библиотеки Python
Красота и изящность стандартной библиотеки PythonКрасота и изящность стандартной библиотеки Python
Красота и изящность стандартной библиотеки Python
 
Algo 00
Algo 00Algo 00
Algo 00
 
[JAM 1.1] Clean Code (Paul Malikov)
[JAM 1.1] Clean Code (Paul Malikov)[JAM 1.1] Clean Code (Paul Malikov)
[JAM 1.1] Clean Code (Paul Malikov)
 
Лекция #5. Введение в язык программирования Python 3
Лекция #5. Введение в язык программирования Python 3Лекция #5. Введение в язык программирования Python 3
Лекция #5. Введение в язык программирования Python 3
 
Производительность в Django
Производительность в DjangoПроизводительность в Django
Производительность в Django
 
Лекция 8. Intel Threading Building Blocks
Лекция 8. Intel Threading Building BlocksЛекция 8. Intel Threading Building Blocks
Лекция 8. Intel Threading Building Blocks
 
DSLs в Perl
DSLs в PerlDSLs в Perl
DSLs в Perl
 
C++ refelection and cats
C++ refelection and catsC++ refelection and cats
C++ refelection and cats
 
Основы языка R
Основы языка RОсновы языка R
Основы языка R
 
Лекция 5. Встроенные коллекции и модуль collections.
Лекция 5. Встроенные коллекции и модуль collections.Лекция 5. Встроенные коллекции и модуль collections.
Лекция 5. Встроенные коллекции и модуль collections.
 
Лекция 8: Многопоточное программирование: Intel Threading Building Blocks
Лекция 8: Многопоточное программирование: Intel Threading Building BlocksЛекция 8: Многопоточное программирование: Intel Threading Building Blocks
Лекция 8: Многопоточное программирование: Intel Threading Building Blocks
 
8 встреча — Язык программирования Python (В. Ананьев)
8 встреча — Язык программирования Python (В. Ананьев)8 встреча — Язык программирования Python (В. Ананьев)
8 встреча — Язык программирования Python (В. Ананьев)
 
Charming python sc2-8
Charming python sc2-8Charming python sc2-8
Charming python sc2-8
 
msumobi2. Лекция 2
msumobi2. Лекция 2msumobi2. Лекция 2
msumobi2. Лекция 2
 
PHP Tricks
PHP TricksPHP Tricks
PHP Tricks
 
Лекция 2. Всё, что вы хотели знать о функциях в Python.
Лекция 2. Всё, что вы хотели знать о функциях в Python.Лекция 2. Всё, что вы хотели знать о функциях в Python.
Лекция 2. Всё, что вы хотели знать о функциях в Python.
 
Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014
Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014
Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014
 
Объектно-ориентированное программирование. Лекции 9 и 10
Объектно-ориентированное программирование. Лекции 9 и 10Объектно-ориентированное программирование. Лекции 9 и 10
Объектно-ориентированное программирование. Лекции 9 и 10
 
ПВТ - осень 2014 - Лекция 4 - Стандарт POSIX Threads. Реентерабельность. Сигн...
ПВТ - осень 2014 - Лекция 4 - Стандарт POSIX Threads. Реентерабельность. Сигн...ПВТ - осень 2014 - Лекция 4 - Стандарт POSIX Threads. Реентерабельность. Сигн...
ПВТ - осень 2014 - Лекция 4 - Стандарт POSIX Threads. Реентерабельность. Сигн...
 
Python
PythonPython
Python
 

More from delimitry

Python Hashlib & A True Story of One Bug
Python Hashlib & A True Story of One BugPython Hashlib & A True Story of One Bug
Python Hashlib & A True Story of One Bug
delimitry
 
JIT compilation for CPython
JIT compilation for CPythonJIT compilation for CPython
JIT compilation for CPython
delimitry
 
Data storage systems
Data storage systemsData storage systems
Data storage systems
delimitry
 
Fuzzing python modules
Fuzzing python modulesFuzzing python modules
Fuzzing python modules
delimitry
 
Writing file system in CPython
Writing file system in CPythonWriting file system in CPython
Writing file system in CPython
delimitry
 
CPython logo
CPython logoCPython logo
CPython logo
delimitry
 
Contribute to CPython
Contribute to CPythonContribute to CPython
Contribute to CPython
delimitry
 
Buzzword poem generator in Python
Buzzword poem generator in PythonBuzzword poem generator in Python
Buzzword poem generator in Python
delimitry
 
True stories on the analysis of network activity using Python
True stories on the analysis of network activity using PythonTrue stories on the analysis of network activity using Python
True stories on the analysis of network activity using Python
delimitry
 
Numbers obfuscation in Python
Numbers obfuscation in PythonNumbers obfuscation in Python
Numbers obfuscation in Python
delimitry
 
ITGM #9 - Коварный CodeType, или от segfault'а к работающему коду
ITGM #9 - Коварный CodeType, или от segfault'а к работающему кодуITGM #9 - Коварный CodeType, или от segfault'а к работающему коду
ITGM #9 - Коварный CodeType, или от segfault'а к работающему коду
delimitry
 
Python dictionary : past, present, future
Python dictionary: past, present, futurePython dictionary: past, present, future
Python dictionary : past, present, future
delimitry
 
Разработка фреймворка на Python для автоматизации тестирования STB боксов
Разработка фреймворка на Python для автоматизации тестирования STB боксовРазработка фреймворка на Python для автоматизации тестирования STB боксов
Разработка фреймворка на Python для автоматизации тестирования STB боксов
delimitry
 
SchoolCTF 2012 - Tpircsavaj
SchoolCTF 2012 - TpircsavajSchoolCTF 2012 - Tpircsavaj
SchoolCTF 2012 - Tpircsavaj
delimitry
 
SchoolCTF 2012 - See Shark
SchoolCTF 2012 - See SharkSchoolCTF 2012 - See Shark
SchoolCTF 2012 - See Shark
delimitry
 
SchoolCTF 2012 - Rings
SchoolCTF 2012 - RingsSchoolCTF 2012 - Rings
SchoolCTF 2012 - Rings
delimitry
 
SchoolCTF 2012 - Bin Pix
SchoolCTF 2012 - Bin PixSchoolCTF 2012 - Bin Pix
SchoolCTF 2012 - Bin Pix
delimitry
 
SchoolCTF 2012 - Acid
SchoolCTF 2012 - AcidSchoolCTF 2012 - Acid
SchoolCTF 2012 - Acid
delimitry
 
Python GC
Python GCPython GC
Python GC
delimitry
 

More from delimitry (19)

Python Hashlib & A True Story of One Bug
Python Hashlib & A True Story of One BugPython Hashlib & A True Story of One Bug
Python Hashlib & A True Story of One Bug
 
JIT compilation for CPython
JIT compilation for CPythonJIT compilation for CPython
JIT compilation for CPython
 
Data storage systems
Data storage systemsData storage systems
Data storage systems
 
Fuzzing python modules
Fuzzing python modulesFuzzing python modules
Fuzzing python modules
 
Writing file system in CPython
Writing file system in CPythonWriting file system in CPython
Writing file system in CPython
 
CPython logo
CPython logoCPython logo
CPython logo
 
Contribute to CPython
Contribute to CPythonContribute to CPython
Contribute to CPython
 
Buzzword poem generator in Python
Buzzword poem generator in PythonBuzzword poem generator in Python
Buzzword poem generator in Python
 
True stories on the analysis of network activity using Python
True stories on the analysis of network activity using PythonTrue stories on the analysis of network activity using Python
True stories on the analysis of network activity using Python
 
Numbers obfuscation in Python
Numbers obfuscation in PythonNumbers obfuscation in Python
Numbers obfuscation in Python
 
ITGM #9 - Коварный CodeType, или от segfault'а к работающему коду
ITGM #9 - Коварный CodeType, или от segfault'а к работающему кодуITGM #9 - Коварный CodeType, или от segfault'а к работающему коду
ITGM #9 - Коварный CodeType, или от segfault'а к работающему коду
 
Python dictionary : past, present, future
Python dictionary: past, present, futurePython dictionary: past, present, future
Python dictionary : past, present, future
 
Разработка фреймворка на Python для автоматизации тестирования STB боксов
Разработка фреймворка на Python для автоматизации тестирования STB боксовРазработка фреймворка на Python для автоматизации тестирования STB боксов
Разработка фреймворка на Python для автоматизации тестирования STB боксов
 
SchoolCTF 2012 - Tpircsavaj
SchoolCTF 2012 - TpircsavajSchoolCTF 2012 - Tpircsavaj
SchoolCTF 2012 - Tpircsavaj
 
SchoolCTF 2012 - See Shark
SchoolCTF 2012 - See SharkSchoolCTF 2012 - See Shark
SchoolCTF 2012 - See Shark
 
SchoolCTF 2012 - Rings
SchoolCTF 2012 - RingsSchoolCTF 2012 - Rings
SchoolCTF 2012 - Rings
 
SchoolCTF 2012 - Bin Pix
SchoolCTF 2012 - Bin PixSchoolCTF 2012 - Bin Pix
SchoolCTF 2012 - Bin Pix
 
SchoolCTF 2012 - Acid
SchoolCTF 2012 - AcidSchoolCTF 2012 - Acid
SchoolCTF 2012 - Acid
 
Python GC
Python GCPython GC
Python GC
 

Python dict: прошлое, настоящее, будущее

  • 1. Python dictionary прошлое, настоящее, будущее Dmitry Alimov Senior Software Engineer Zodiac Interactive 2016 SPb Python Interest Group
  • 3. >>> d = {} # то же самое, что d = dict() >>> d['a'] = 123 >>> d['b'] = 345 >>> d['c'] = 678 >>> d {'a': 123, 'c': 678, 'b': 345} >>> d['b'] 345 >>> del d['c'] >>> d {'a': 123, 'b': 345}
  • 4. Ключами словаря могут быть значения только hashable типов >>> d[list()] = 1 Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: unhashable type: 'list' >>> d[set()] = 2 Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: unhashable type: 'set' >>> d[dict()] = 3 Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: unhashable type: 'dict' Все иммутабельные built-in’ы – hashable
  • 5. import random class A(object): def __init__(self, index): self.index = index def __eq__(self, other): return True def __hash__(self): return random.randint(0, 3) def __repr__(self): return 'A%d' % self.index d = {A(0): 0, A(1): 1, A(2): 2} print('keys: %s' % d.keys()) print('values: %s' % d.values()) for k in d: print('%s = %s' % (k, d.get(k, 'not found'))) Random hash – плохая идея Запуск 1 keys: [A1, A2, A0] values: [1, 2, 0] A1 = 1 A2 = not found A0 = 0 Запуск 2 keys: [A1, A0] values: [2, 0] A1 = not found A0 = not found
  • 7. Ячейка в хэш-таблице может иметь три состояния: 1) Неиспользованная 2) Активная 3) Пустая typedef struct { Py_ssize_t me_hash; PyObject *me_key; PyObject *me_value; } PyDictEntry; - Хэш-таблица - Разрешения коллизий методом открытой адресации - Начальный размер = 8 - Коэффициент заполнения = 2/3 - Коэффициент роста = 4 или 2 (зависит от числа используемых ячеек) - “/Include/dictobject.h”, “/Objects/dictobject.c”, “/Objects/dictnotes.txt” Словарь в CPython >2.1
  • 8. ma_fill – сумма «активных» и «пустых» ячеек ma_used – число «активных» ячеек ma_mask – маска, равная PyDict_MINSIZE - 1 ma_lookup – функция поиска (по умолчанию lookdict_string) #define PyDict_MINSIZE 8 typedef struct _dictobject PyDictObject; struct _dictobject { PyObject_HEAD Py_ssize_t ma_fill; Py_ssize_t ma_used; Py_ssize_t ma_mask; PyDictEntry *ma_table; PyDictEntry *(*ma_lookup)(PyDictObject *mp, PyObject *key, long hash); PyDictEntry ma_smalltable[PyDict_MINSIZE]; };
  • 9. Нужны хорошие хэш-функции >>> map(hash, [0, 1, 2, 3, 4]) [0, 1, 2, 3, 4] >>> map(hash, ['abca', 'abcb', 'abcc', 'abcd', 'abce']) [1540938117, 1540938118, 1540938119, 1540938112, 1540938113] Модифицированная хэш-функция FNV (Fowler–Noll–Vo) для строк Ключ “-R” интерпретатора для псевдо-случайной соли (строки, bytes и объекты datetime) >>> map(hash, ['abca', 'abcb', 'abcc', 'abcd', 'abce']) [-218138032, -218138029, -218138030, -218138027, -218138028] Хэш-функции
  • 10. Разрешение коллизий Коллизия – ситуация, при которой разные входные значения (ключи) имеют одинаковое значение хэша. Процедура выбора подходящей ячейки для вставки элемента в хэш-таблицу называется пробирование, а рассматриваемая ячейка-кандидат – проба. В CPython – используется пробирование с псевдослучайным шагом PERTURB_SHIFT = 5 perturb = hash(key) while True: j = (5 * j) + 1 + perturb perturb >>= PERTURB_SHIFT index = j % 2**i См. “/Objects/dictobject.c” В CPython <2.2 использовался расчёт индекса основанный на многочленах
  • 11. >>> PyDict_MINSIZE = 8 >>> key = 123 >>> hash(key) % PyDict_MINSIZE >>> 3 Расчѐт индекса >>> mask = PyDict_MINSIZE - 1 >>> hash(key) & mask >>> 3 Вместо деления по модулю используется логическая операция «И» и маска Так получаются младшие биты хэша: 2 ** i = PyDict_MINSIZE, отсюда i = 3, т.е. достаточно трёх младших бит hash(123) = 123 = 0b1111011 mask = PyDict_MINSIZE - 1 = 8 - 1 = 7 = 0b111 index = hash(123) & mask = 0b1111011 & 0b111 = 0b011 = 3
  • 12. mask = PyDict_MINSIZE - 1 index = hash(123) & mask Целые
  • 13. Строки mask = PyDict_MINSIZE - 1 index = hash(123) & mask
  • 14. Словарь в CPython >2.1 Инициализация словаря Добавление элемента PyDict_SetItem() PyDict_New() ma_used = 0 ma_fill = 0 ma_mask = PyDict_MINSIZE – 1 ma_table = ma_smalltable ma_lookup = lookdict_string insertdict() ma_used += 1 ma_fill += 1 dictresize() если ma_fill >= 2/3 * size Удаление элемента PyDict_DelItem() ma_used -= 1
  • 20. perturb = -1297030748 # i = (i * 5) + 1 + perturb i = (4 * 5) + 1 + (-1297030748) = -1297030727 index = -1297030727 & 7 = 1 hash('!!!') = -1297030748 i = -1297030748 & 7 = 4 # perturb = perturb >> PERTURB_SHIFT perturb = -1297030748 >> 5 = -40532211 # i = (i * 5) + 1 + perturb i = (-1297030727 * 5) + 1 + (-40532211) = -6525685845 index = -6525685845 & 7 = 3
  • 21. >>> d {'python': 2, 'article': 4, '!!!': 5, 'dict': 3, 'a key': 1} >>> d.__sizeof__() 248 Добавление элемента
  • 22. Измерение размера хэш-таблицы >>> d {'!!!': 5, 'python': 2, 'dict': 3, 'a key': 1, 'article': 4, ';)': 6} >>> d.__sizeof__() 1016
  • 23. Измерение размера хэш-таблицы /* Find the smallest table size > minused. */ for (newsize = 8; newsize <= minused && newsize > 0; newsize <<= 1) ; ... } dictresize(PyDictObject *mp, Py_ssize_t minused) { ... PyDict_SetItem(...) { ... dictresize(mp, (mp->ma_used > 50000 ? 2 : 4) * mp->ma_used); ... } В нашем примере: ma_fill = 6 > (8 * 2 / 3) ma_used = 6 отсюда minused = 4 * 6 = 24, следовательно newsize = 32
  • 24. Порядок добавления ключей >>> d1 = {'one': 1, 'two': 2, 'three': 3, 'four': 4, 'five': 5} >>> d2 = {'three': 3, 'two': 2, 'five': 5, 'four': 4, 'one': 1} >>> d1 == d2 True >>> d1.keys() ['four', 'three', 'five', 'two', 'one'] >>> d2.keys() ['four', 'one', 'five', 'three', 'two'] Индексы добавляемых в словарь элементов зависят от находящихся в нём элементов
  • 25. >>> 7.0 == 7 == (7+0j) True >>> d = {} >>> d[7.0] = 'float' >>> d {7.0: 'float'} >>> d[7] = 'int' >>> d {7.0: 'int'} >>> d[7+0j] = 'complex' >>> d {7.0: 'complex'} >>> type(d.keys()[0]) <type 'float'> int, float, complex >>> hash(7) 7 >>> hash(7.0) 7 >>> hash(7+0j) 7
  • 26. >>> d = {'a': 1} >>> for i in d: ... d['new item'] = 123 ... Traceback (most recent call last): File "<stdin>", line 1, in <module> RuntimeError: dictionary changed size during iteration Добавление элемента во время итерации
  • 27. Удаление элемента dummy = PyString_FromString("<dummy key>"));
  • 29. Интересный случай ma_fill = 6 > (8 * 2 / 3) dictresize()
  • 30. Интересный случай ma_fill = 6 > (8 * 2 / 3) ma_used = 1 отсюда minused = 4 * 1 = 4, следовательно newsize = 8
  • 31. Кэш PyDictEntry ma_smalltable[8]; На x86 с линейкой кэша в 64 байта – в одну линейку входит: 64 / (4 * 3) = 5.33 элементов PyDictEntry typedef struct { Py_ssize_t me_hash; PyObject *me_key; PyObject *me_value; } PyDictEntry; Оптимизация локальности и коллизии См. “/Objects/dictnotes.txt” Источник Время доступа Кэш L1 1 нс Кэш L2 4 нс RAM 100 нс
  • 32. Открытая адресация vs метод цепочек Хотя тут линейное пробирование, а не псевдослучайное как в CPython.
  • 33. OrderedDict from collections import OrderedDict - Внутренний словарь - Кольцевой (циклический, замкнутый) двусвязный список - “/Lib/collections/__init__.py”
  • 35. Словарь в CPython 3.5 - PEP 412 - Key-Sharing Dictionary - Может быть в одной из двух форм: комбинированная таблица и сплит-таблица - Начальный размер = 4 (сплит-таблица) или 8 (комбинированная таблица) - Максимальное заполнение = (2*n+1)/3 - Коэффициент роста = used*2 + capacity/2 - “/Objects/dict-common.h”, “/Include/dictobject.h”, “/Objects/dictobject.c”, “/Objects/dictnotes.txt” typedef struct { Py_hash_t me_hash; PyObject *me_key; PyObject *me_value; /* only meaningful for combined tables */ } PyDictKeyEntry; struct _dictkeysobject { Py_ssize_t dk_refcnt; Py_ssize_t dk_size; dict_lookup_func dk_lookup; Py_ssize_t dk_usable; PyDictKeyEntry dk_entries[1]; }; typedef struct { PyObject_HEAD Py_ssize_t ma_used; PyDictKeysObject *ma_keys; PyObject **ma_values; } PyDictObject;
  • 36. Комбинированная таблица vs cплит-таблица Комбинированная таблица - Для хранения всех явно созданных словарей (dict() и {}) - ma_values = NULL - Никогда не может стать сплит-таблицей Сплит-таблица - Для хранения __dict__ объектов - Ключи – только строки - Отдельная таблица для значений (ma_values) - После изменения размера превращается в комбинированную (но если изменение размера происходит из-за setattr и существует только один экземпляр класса, происходит ре-сплит) - Для поиска используется lookdict_split
  • 37. Словарь в CPython 3.5 Новое состояние ячейки в хэш-таблице для сплит-таблицы: 1) Неиспользованная 2) Активная 3) Пустая 4) Ожидающая (me_key != NULL, me_key != dummy и me_value == NULL) typedef struct { Py_hash_t me_hash; PyObject *me_key; PyObject *me_value; /* only meaningful for combined tables */ } PyDictKeyEntry;
  • 38. Сплит-таблица Начальный размер = 4 Максимальное заполнение = (2*n+1)/3 = (2*4+1)/3 = 3, то есть изначально ma_keys->dk_usable = 3
  • 39. Сплит-таблица class A(): def __init__(self): self.a = 1 self.b = 2 self.c = 3 a = A() print(a.__dict__.__sizeof__()) # 72 setattr(a, 'd', 4) # респлит print(a.__dict__.__sizeof__()) # 168 print({}.__sizeof__()) # 264 Начальный размер = 4 Максимальное заполнение = (2*n+1)/3 = (2*4+1)/3 = 3 Коэффициент роста = used*2 + capacity/2 = 3*2 + 4/2 = 8, отсюда minused = 8, следовательно newsize = 16 (см. dictresize)
  • 40. class A(): def __init__(self): self.a = 1 self.b = 2 self.c = 3 a = A() print(a.__dict__.__sizeof__()) # 72 b = A() setattr(a, 'd', 4) # респлита нет из-за b print(a.__dict__.__sizeof__()) # 456 Сплит-таблица Сплит-таблица превратилась в комбинированную таблицу
  • 41. Ключевые отличия от CPython 2.x: - Таблица может быть разделена на Ключи и Значения - Добавлено новое состояние ячейки - Больше нет ma_smalltable в структуре - Обычные словари стали немного больше - Выигрыш в памяти до 60% для программ, использующих много ООП (в соответствии с https://github.com/python/cpython/blob/3.5/Objects/dictnotes.txt) Всё ещё случаются баги типа: Unbounded memory growth resizing split-table dicts (https://bugs.python.org/issue28147) Резюме
  • 42. Хэш-функции в CPython 3.5 SipHash для строк (>= CPython 3.4) - Стойкий к hash-flooding DoS атакам - Успешно используется во многих других языках Немного изменённые хэш-функции для float, int PEP 456 – Secure and interchangeable hash algorithm hash(float("+inf")) == 314159, hash(float("-inf")) == -314159, а было -271828
  • 43. OrderedDict в CPython 3.5 - Двусвязный список - Хэш таблица od_fast_nodes c зеркальным отображением словаря od_dict - “/Include/odictobject.h”, “/Objects/odictobject.c”
  • 45. Словарь в PyPy - Начиная с PyPy 2.5.0 по умолчанию – ordereddict - Начальный размер 16 - Коэффициент заполнения до 2/3 - Коэффициент роста 4 (до 30000 элементов) или 2 - При удалении множества элементов выполняется уплотнение - “/rpython/rtyper/lltypesystem/rordereddict.py” struct dicttable { int num_live_items; int num_ever_used_items; int resize_counter; variable_int *indexes; // byte, short, int, long dictentry *entries; ... } struct dictentry { PyObject *key; PyObject *value; long hash; bool valid; }
  • 46. Словарь в PyPy struct dicttable { variable_int *indexes; dictentry *entries; ... } FREE = 0 DELETED = 1 VALID_OFFSET = 2
  • 47. PyDictionary в Jython - Построен на ConcurrentHashMap - Разрешения коллизий методом цепочек (separate chaining) - Начальный размер = 16, коэффициент заполнения = 0.75, коэффициент роста = 2 - Сегменты и потокобезопасность
  • 48. PythonDictionary в IronPython - Построен на Dictionary (.NET) - Разрешения коллизий методом цепочек - Начальный размер = 0, коэффициент заполнения = 1.0 - Рехэшинг в случае если число коллизий >= 100 - Коэффициент роста = 2 (новый размер равен ближайшему большему простому числу) из ряда primes = {3, 7, 11, 17, 23, 29, 37, 47, 59, 71, 89, 107,… , 4999559, 5999471, 7199369}
  • 51. Словарь в CPython 3.6 typedef struct { Py_hash_t me_hash; PyObject *me_key; PyObject *me_value; /* only meaningful for combined tables */ } PyDictKeyEntry; typedef struct { PyObject_HEAD Py_ssize_t ma_used; /* number of items in the dictionary */ uint64_t ma_version_tag; /* unique, changes when dict modified */ PyDictKeysObject *ma_keys; PyObject **ma_values; } PyDictObject; - Добавили версию ma_version_tag (PEP 509 – Add a private version to dict) - Начальный размер = 8 (для сплит-таблицы тоже) - Максимальное заполнение = (2*n)/3 - Добавил INADA Naoki в https://bugs.python.org/issue27350 Состояния ячеек в хэш-таблице: 1) Неиспользованная (index == DKIX_EMPTY == -1) 2) Активная (index >= 0 , me_key != NULL и me_value != NULL) 3) Пустая (index == DKIX_DUMMY == -2, только для комбинированных таблиц) 4) Ожидающая (index >= 0 , me_key != NULL и me_value == NULL, только для сплит-таблиц)
  • 52. Словарь в CPython 3.6 - Добавили dk_nentries и dk_indices struct _dictkeysobject { Py_ssize_t dk_refcnt; Py_ssize_t dk_size; /* Size of the hash table (dk_indices) */ dict_lookup_func dk_lookup; /* Function to lookup in dk_indices */ Py_ssize_t dk_usable; /* Number of usable entries in dk_entries */ Py_ssize_t dk_nentries; /* Number of used entries in dk_entries */ union { int8_t as_1[8]; int16_t as_2[4]; int32_t as_4[2]; #if SIZEOF_VOID_P > 4 int64_t as_8[1]; #endif } dk_indices; PyDictKeyEntry dk_entries[dk_usable]; /* using DK_ENTRIES macro */ };
  • 53. Словарь в CPython 3.6 (Комбинированная таблица)
  • 54. Ключевые отличия от CPython 3.5: - Добавили dk_indices с типом, зависящим от размера - Добавили версию ma_version_tag (PEP 509) - Изменили начальный размер для сплит-таблицы на 8 - Изменили максимальное заполнение на (2*n)/3 - При удалении из сплит-таблицы она становится комбинированной - Решена проблема сохранения порядка **kwargs (PEP 468) - Решена проблема сохранения порядка атрибутов класса (PEP 520) - Использование памяти на 20-25% меньше по сравнению с CPython 3.5 (https://docs.python.org/3.6/whatsnew/3.6.html#other-language-changes) Резюме
  • 55. Ссылки 1. Реализация словаря в Python 2.7 https://habrahabr.ru/post/247843/ 2. Python hash calculation algorithms http://delimitry.blogspot.com/2014/07/python-hash-calculation-algorithms.html 3. PEP 412 - Key-Sharing Dictionary https://www.python.org/dev/peps/pep-0412/ 4. PEP 456 - Secure and interchangeable hash algorithm https://www.python.org/dev/peps/pep-0456/ 5. Mirror of the CPython repository https://github.com/python/cpython/ 6. Faster, more memory efficient and more ordered dictionaries on PyPy https://morepypy.blogspot.ru/2015/01/faster- more-memory-efficient-and-more.html 7. PyDictionary (Jython API documentation) http://www.jython.org/javadoc/org/python/core/PyDictionary.html 8. Jython repository https://bitbucket.org/jython/jython 9. Теория и практика Java: Построение лучшей HashMap http://www.ibm.com/developerworks/ru/library/j-jtp08223/ 10. Back to basics: Dictionary part 2, .NET implementation https://blog.markvincze.com/back-to-basics-dictionary-part-2- net-implementation/ 11. http://referencesource.microsoft.com/mscorlib/system/collections/generic/dictionary.cs.html 12. https://github.com/IronLanguages/main/blob/ipy-2.7-maint/Languages/IronPython/IronPython/ 13. https://bitbucket.org/pypy/pypy/ 14. https://twitter.com/raymondh 15. PEP 509 - Add a private version to dict https://www.python.org/dev/peps/pep-0509/ 16. Compact and ordered dict http://bugs.python.org/issue27350 17. What’s New In Python 3.6 https://docs.python.org/3.6/whatsnew/3.6.html 18. PEP 468 - Preserving the order of **kwargs in a function https://www.python.org/dev/peps/pep-0468/ 19. PEP 520 - Preserving Class Attribute Definition Order https://www.python.org/dev/peps/pep-0520/ Картинки с сайтов: http://www.rcreptiles.com/blog/index.php/2008/06/28/read_the_operating_manual_first http://kiwigamer450.deviantart.com/art/Back-to-The-Past-Logo-567858767 http://beyondplm.com/wp-content/uploads/2014/04/time-paradox-past-future-present.jpg http://itband.ru/wp-content/uploads/2014/10/Future.jpg https://en.wikipedia.org/wiki/Hash_table
  • 56. Q & A @delimitry spbpython.guru SPb Python Interest Group
  • 58. Разрешение коллизий методом цепочек Разрешение коллизий методом открытой адресации (псевдослучайное пробирование)