SlideShare a Scribd company logo
Хочу всё сжать
Андрей Аксёнов
v.1.1 // highload 2017
disclaimer // 18+ // [R] // уберите детей
• Да, как обычно обзорный доклад, без хардкора
• Упомяну 10-20 методов вскользь, дальше в википедию
• Чуть в детали в 5-7 простых методов, дальше в википедию
• Ну или в коридор (лучше), время до обеда есть, гуляем
• Да, репетировал; нет, в бюджет времени не уложусь
• Speaker rant, доклады не в том порядке!!!
Про сжатие
• Несколько шире архиваторов и даже zlib
• 7zip, rar, tar.bz2 – сжатие
• jpeg, mp3, aac, h264, bpg – сжатие
• gzip, lz4, snappy, brotli, zstd – сжатие
• huffman, ari/range, varint – сжатие
• byte x = byte(float(y)*127) – тоже сжатие!!!
• Бывает таки не только в мире C/C++
• Java, Go, C#, Rust? Python, JavaScript, PHP?
• Причём тут вообще “highload”?...
Сжатие == две ключевых части
• Как мне идеально сжать что_угодно?
1. Хитро преобразовать (или нет)
2. Ловко закодировать (или нет)
• Зачем преобразовывать?
• Чтобы ловчее кодировалось!
• Кодировать это уже небось адски сложно?
• Шокирующие новости – НЕТ!
• И, собственно, код – фундамент успеха
• В отличие от IT и даже программирования, kekeke
Любой код за 1 минуту
• Buy low, sell high?
• Круглое носить, квадратное катать?
• Частое паковать, редкое раздувать
• Причем, речь всегда о числах (и битах/байтах)
• Классика: Huffman, Golomb, Rice, Elias, ari/range
• Поновей: varint, S9/S16, PFOR
• Важно: свой личный DIY нередко зажжот
Что значит “частое”?
• Например, английские тексты, pg.txt
• Буквы a-z ожидаемо чаще всех других
• 336.2m всего
• 242.0m [a-z]
• 31.0m [e]
• 21.9m [t]
• 19.4m [a]
• …
• 8.1m [A-Z]
Что значит “частое”?
• Например, английские тексты, pg.txt
• Байты a-z тоже сильно чаще всех других
• 336.2m всего
• 242.0m [a-z]
• 54.1m [ ] самый частый байт (да, пробел)
• 31.0m [e]
• …
Наш первый (тупой) код
• Пробел = 1 бит, “0”, и так 54.1m раз
• Остальное = 9 бит, “1 vvvvvvvv”, 282.1m раз
• -378.7 mbit = -7 bit * 54.1m
• +282.1 mbit = +1 bit * 282.1m
• -12.0 mbyte = -96.6 = -378.7+282.1 mbit
• Наш первый профит – ура!!!
• Уже плюс сжатие, но всего на 3.6% :(
• Минус случайный доступ, “плюс” бииииты
Почему так плохо?!
• Мало сэкономили, достаточно проиграли
• Надо экономить на N частых байтах, а не 1
• Давайте занумеруем байты по частоте
• 1 = ‘ ‘
• 2 = ‘e’
• 3 = ‘t’
• 4 = ‘a’
• …
• Надо передать таблицу = 256 байт, фигня
Наш второй код
• Давайте номер #1 кодировать в 1 бит “1”
• Давайте остальные номера кодировать
• Минимальным нужным числом бит B
• Которое кодировать цепочкой (B-1) нулей
• #1 = “1” 1 бит ‘ ‘
• #2 = “0 10” 3 бита ‘e’
• #3 = “0 11” 3 бита ‘t’
• #4 = “00 100” 5 бит ‘a’
Не совсем наш код
• Результаты кодирования?
• #1, 8 => 1 бит
• #2-3, 8 => 3 бита
• #4-7, 8 => 5 бит
• #8-15, 8 => 7 бит
• #16-256, 8 => 9, 11, …, 17 бит
• Тем не менее, 336.2mb => 231.0mb, 68.7%
• Это, кстати, Elias gamma code [Elias1975]
• Но, конечно, бииииты
Наш третий код
• Давайте #1-8 кодировать в 4 бита, “1 vvv”
• Давайте #9-135 в 8 бит, “0 ddd dddd”, d = v-9
• Давайте #136-256 в 16 бит, “0 111 1111” V
• ref 336.2mb 100.0%
• v2-gamma 231.0mb 68.7%
• v3-nibble 232.0mb 69.0%
• Чёрт, чуток проиграли дедушке Elias
• Но, зато больше нету битов! (побитного чтения)
Наш третий код
• Кстати, а если #136-256 ваще в 256 бит?!
• v3-nibble 232.0mb, 69.0%
• v3-nibble-megaloss 232.4mb, 69.1%
Наш третий код
• Кстати, а если #136-256 ваще в 256 бит?!
• v3-nibble 232.0mb, 69.0%
• v3-nibble-megaloss 232.4mb, 69.1%
Наш третий код – важное замечание
• Кстати, а если #136-256 ваще в 256 бит?!
• v3-nibble 232.0mb, 69.0%
• v3-nibble-megaloss 232.4mb, 69.1%
• Так будет НЕ ВСЕГДА
• Но здесь – вот такая статистика
• И – не только здесь
Почему побитно – отстой?
int get_byte() {
return m_buf[m_pos++];
}
Почему побитно – отстой?
int get_nibble() {
return (m_pos & 1)
? m_buf[m_pos++ >> 1] & 15
: (m_buf[m_pos++ >> 1] >> 4);
}
Почему побитно – отстой?
int get_bits(int n) {
static const byte mask[8] = {1,3,7,15,31,63,127,255};
int r = 0, s = 0, pmax = m_pos + n;
while (m_pos < pmax) {
int b = (m_pos & 7);
int got = min(pmax – m_pos, 8 – pbit);
r += ((m_buf[m_pos >> 3] >> b) & mask[got]) << s;
m_pos += got;
s += got;
}
return r;
} // probably buggy like hell too
Почему побитно – отстой?
int get_bits(int n) {
int64_t v = *(int64_t*)(m_buf + (m_pos >> 3));
v >>= (m_pos & 7);
m_pos += n;
return int(v & ((1ULL << n) – 1));
}
// needs a post-buffer gap, of course
Почему побитно – отстой?
int get_bits(int n) {
int64_t v = *(int64_t*)(m_buf + (m_pos >> 3));
v >>= (m_pos & 7);
m_pos += n;
return int(v & ((1ULL << n) – 1));
}
// ...vs...
int get_byte() {
return *m_ptr++;
}
А можно ли тогда – байтами?!
• Можно!!!
• Тут – не выйдет, т.к. байты и кодируем
• Лежит хомяк, на нём хомяк…
• Там – выйдет, где числа побольше
• Например, произвольные int, до 32 бит
• Например, длины строк
• Например, строки-то часто, кхм, недлинные
Наш четвертый код
• Кодируем произвольную длину, 4 байта
• Кодируем по 1 байту за раз, “M VVV VVVV”
• Верхний 1 бит, m == 0 конец, 1 не конец
• Остальные 7 бит, vvvvvvv == кусок значения
• Длины 0-127 == 1 байт
• Длины 128-16383 == 2 байта, и т.д.
• Длины 268 435 456+ == 5 байт, конечно
• Это, кстати, VarInt (всенародно любимый)
За что его любить!?
int decode_varint() {
int r = *m_ptr & 127;
while (*m_ptr++ & 128)
r = (r << 7) + (*m_ptr & 127);
return r;
}
// btw, 500+ mb/sec, 5+ yr/ago; also, JS
Наш пятый код
• Кодируем любой честный int (напр. длина)
• Значения 0-251 кладем как есть == 1 байт
• Иначе 252-255 == “ща поедет 1-4 байта”
• 173 => {173}
• 253 => {252, 253}
• 0xABCD => {253, 0xCD, 0xAB} // native order
• Длины 252+ == 2-5 байт, всегда +1 лишний
• Это, кстати, MySQL (wire protocol, at least)
Давай уж опять свой декодер
int decode_mysql() {
int *p = (int*)++m_ptr;
switch (m_ptr[-1]) {
case 252: return *p & 0xff;
case 253: return *p & 0xffff;
case 254: return *p & 0xffffff;
case 255: return *p;
default: return m_ptr[-1];
} // did not bench, but fast
Мне скучно и я пожалуй пойду
• Стоять, мы уже вообще научились кодировать
• Ну, почти
• Конечно, есть еще 10+ методов, и 2-3+ крутых (GVI, S9, PFOR…)
• Но для начала хватит и этих, и они вас не подведут
• Но таки давайте чуть про “преобразовывать”
• Обзорно, без исходников уже
• Ну, почти
Дискотека 1970х
• А вот у меня pg.txt, там текст
• [Gutenberg] в позициях 11, 307 итд в pg.txt
• Давайте в позиции 11 сохраним сами байты
• cmd = 0 (literal), len = 9, data = [Gutenberg] => +1 byte
Дискотека 1970х
• А вот у меня pg.txt, там текст
• [Gutenberg] в позициях 11, 307 итд в pg.txt
• Давайте в позиции 11 сохраним сами байты
• cmd = 0 (literal), len = 9, data = [Gutenberg] => +1 byte
• Давайте в позиции 307 сохраним ссылку
• cmd = 1 (match), len = 9, data = 296 => -6 bytes
Дискотека 1970х
• А вот у меня pg.txt, там текст
• [Gutenberg] в позициях 11, 307 итд в pg.txt
• Давайте в позиции 11 сохраним сами байты
• cmd = 0 (literal), len = 9, data = [Gutenberg] => +1 byte
• Давайте в позиции 307 сохраним ссылку
• cmd = 1 (match), len = 9, data = 296 => -6 bytes
• Все циферки как-то кодируем, мы ж умеем
• Например ((len << 1) + command) вместе! чтобы +1, а не +2 там вверху
• Ура, мы изобрели семейство LZ (Lempel-Ziv)
Дискотека 1980х
• А вот у меня pg.txt, там текст
• Увидели в потоке байт X, а далее увидели Y
• Вероятность увидеть именно Y зависит от X
• “ВЕ” чаще, “ВЬ” реже, “ЕЬ” вообще нету в языке
• Давайте сделаем 256 таблиц по 256 счетчиков
Почему так плохо?!
• Мало сэкономили, достаточно проиграли
• Надо экономить на N частых байтах, а не 1
• Давайте занумеруем байты по частоте
• 1 = ‘ ‘
• 2 = ‘e’
• 3 = ‘t’
• 4 = ‘a’
• …
• Надо передать таблицу = 256 байт, фигня
Дискотека 1980х
• А вот у меня pg.txt, там текст
• Увидели в потоке байт X, а далее увидели Y
• Вероятность увидеть именно Y зависит от X
• “ВЕ” чаще, “ВЬ” реже, “ЕЬ” вообще нету в языке
• Давайте сделаем 256 таблиц по 256 счетчиков
• Давайте обновлять их на лету (чтобы 1 проход)
• Давайте вместо Y кодировать номер Y в table[X]
• Ура, мы изобрели простейший PPM(1)
• А если контекст X это N байт, то PPM(N)
Дискотека 1990х, 2000х
• Продвижения кое-какие были, да
• Внимание не заостряем, нет – времени нет :P
• Считаем, что в святые 90е реализовали и отполировали
Дискотека 2010х
• А вот у меня pg.txt, там текст
• Давайте заведем словарь!!!
• Увидели слово [project], закодировали номер в словаре
• Увидели незнакомое, закодировали “как в LZ”
• Ну нет, не совсем как LZ, ладно, вру
• Ещё “модель контекста” какая-то, непонятно, в вики глянем
• Ура, мы изобрели Brotli
• А пока мы его изобретали, уже полвеба умеет (якобы)
Как же “преобразовывать”?
• Внезапно, теперь мы можем всё!!!
• Gzip = LZ77 + Huffman
• 7zip = LZMA / PPM + range coder (грубо говоря)
• Lz4 = LZ77 + DIY_code
• byte marker = (literal_len << 4) + match_len
• byte literal [ literal_len ]
• short offset // от текущей позиции назад
• Zstd = LZ77 + tANS entropy coder
• Brotli = LZ77 + Huffman + context modeling + dict
• …
А если там не текст?
• А у меня поиск, а там (удивление!) не текст!
• А там слово “hello” => список номеров
• {12, 31, 79, 139, 517, …, 1234567, 1234568, 1234569, …}
• Номера всегда растут
• Тупо жать каждый номер? Нет!
• Тупо жать дельты между номерами? Да!!!
• {12, 19, 48, 60, 378, …, ?, 1, 1, …} – или даже
• {11, 18, 47, 59, 377, …, ?, 0, 0, …}
• Ура, мы изобрели Lucene, Sphinx, Google... 
Нельзя ли еще лучше?
• А что, если там “стопслово”?
• docs {1,2,3,5,6,9,10,11,12,…} x 10M+ раз
• deltas {1,1,2,2,1,3,1,1,1,…}
• Даже по 1 байту = 8 бит на дельту чота жырно
• А давайте группы по 32 дельты?
• byte nbits; uint deltas[nbits];
• 32x{1} – было 32 байта, стало 1 (!!!) байт
• 32x{1,2,3,4} – было 32, стало 9 байт
• Ура, мы изобрели типа FOR (Frame Of Reference)
Обратите внимание, локальный K
• Оригинал
• 32 docid * 4 / 8 bytes == 128 / 256 bytes
• small-deltas + varint => 32 bytes, k=0.25 / 0.12
• small-deltas + FOR => 9 bytes, k=0.07 / 0.035
• big-deltas так просто не сдаются, конечно
• Поэтому общий результат всегда хуже 
Но совсем хорошо – совсем тупо!
• Пусть max(doc_id) = M – и авось M << 2^32
• Тупо строим bitmap на M бит и всё
• А ещё такие битмапы дико быстро пересекать
• Работает НЕ всегда!
• Пусть слово матчит N документов, типа 3 шт, на M = 10 млн бит
• При N < M/32 будет лучше даже vector<int> без сжатия!!!
• При N < M/8 лучше (?) vector<byte> deltas
• Тупо, но работает, чё (ещё и лучше всех)
• k=0.031 / 0.016, кстати
Как ещё “преобразовывать”?
• Пажжи, а если у меня не текст и не поиск?!
• А вот у меня лог (BIGINT uid, BIGINT tstamp)
• На самом деле колонок больше, но пусть так
• Точность до микросекунды, потому bigint
• Пользователей мало, но мы амбициозны
• Не, tstamp растет (разумеется)
• А далее userid типа рандомный
• Всё, расходимся, такое не сжать?
Сжимаем “несжимаемое”
• Долбим (uid, tstamp) на блоки
• Лучше побольше, 128+ записей
• В каждом блоке пробуем
• RLE == N повторов значения X => {X, N}
• MinDelta == из всех uid вычитаем min(uid)
• BlockDict == если мало разных значений, стром LUT
• DeltaRange == вместо значений кодируем дельты
• CommonDelta == словарь дельт и entropy coder
• …
Сжимаем “несжимаемое”
• Растущие tstamp => вчистую DeltaRange
• “Случайные” uid, ip и т.п. => ну, зависит
• Если чутка неслучайные, что-то сработает
• На крайняк, тупо урезать uid до 5-6 байт :P
• Залетные неслучайные city_id, price, ...
=> ужмутся в разы, до единиц бит, см. K
• Ура, мы изобрели Vertica
• И возможно ClickHouse (но это не точно)
• Но Миловидов пока не опроверг!!!
Я вообще может по ML/DS угораю
• Нейросетка double[512] насчитала
• Памяти в мобиле ваще нет
• Все остальные трюки не работают норм
• Округление == тоже трансформ? Или код?
• short[512] ровно в 4 раза меньше!!!
• Тупо, но работает, чё
• Гусары, ни слова про PCA
Я ничего никогда не храню!!!
• Но переменные-то в программе бывают?
struct Token {
int start, len, pos;
bool is_stop;
int multi_pos_len; // mostly 0
}
// sizeof(Token) == 20
True magic story, bro
• Байка из Sphinx, ага (про snippets)
• Раз, парсим текст – что мееедленно
• Два, обходим токены – ищем лучшие спаны
• Три, обходим токены – генерируем ответ
• Первая реализация, vector<Token>
• Плюс токены были ещё толще, 40+ байт
• С документами в 10 kb все работало отлично
• Но тут прилетел документ в 226000 kb
• Сожрал 4+ gb памяти и упал 
True magic story, bro
• Вторая реализация, жатый поток
• Раз, парсим – и тут же жмем
• Два, обходим – разжимая “по одному”
• Три, опять обходим – опять разжимая
• Ожидания?
• ref жрал, выходит, до ~20x doc_size
• медленно, хотелось уложиться в 2x от ref
• компактно, но чтобы ~1x doc_size
=> на мелких документах был план отключить
True magic story, bro
• Реальность?
• компактнее, чем ожидалось (0.3…0.5 doc_size)
• всегда быстрее, чем ref (чуть не 2x иногда)
• отлично, 1 вариант кода, а не 2 варианта
• В чём обещанная МАГИЯ?
• Быстро // дешево // хорошо, да?
“Треугольник” Выбора – в жизни, без
магии
БЫСТРО
ДЕШЕВО
ХОРОШО
МЫ
Да я вообще не программист!!!
• Программы хоть запускать умеешь?!
• Вчера были gzip, bzip2 – и всё
• Сегодня есть lz4, snappy, brotli, zstd, 7z…
• Сегодня нужно выбирать – всегда есть чего
• Особенно если ты всё же программист
• Что важно – есть кодеки “на скорости RAM”
• Втыкаются везде, см. MySQL, Postgres, …
pg.txt, 336.2 mb, win7-ymmv!
Size
(mb)
Pack
(sec)
Unp
(sec)
v3-nibble 232.0 - -
gzip -1 149.8 7.7 3.4
gzip -9 125.8 28.8 3.0
bzip2 92.8 44.1 29.2
p7z (*) 88.8 109.4 3.8
zstd -3 119.6 3.0 0.8
zstd -9 107.8 16.8 0.8
Size
(mb)
Pack
(sec)
Unp
(sec)
brotli -1 147.7 4.7 2.5
brotli -3 124.2 8.7 1.9
brotli -9 102.9 106.0 1.6
lz4 -1 198.0 0.36 0.23
lz4 -9 145.0 4.5 0.22
…
Зачем вообще был этот
доклад?!?!• “Все” типа умеют в vector/list/hash
• Уметь надо во много больше
• Умеют немногие – а что изучать?
• Сжатие может не понадобиться никогда
• Сжатие может сильно помочь – см. магия
• Сложного там ничего – см. доклад
• Плюс актуальные ключевики – вот они:
keywords
• Как ловко кодировать какие-то числа?
• Elias, Rice, Golomb, Huffman, arithmetic, range coder, ANS family,
FOR, PFOR-delta, S9, S16, varint, group varint, LSIC…
• Как ещё поуменьшить данные?
• roundoff, bitmaps, deltas, LUTs, dicts, LZ77/78, LZW, LZMA, PPM, PAQ,
context modeling…
• Какую подцепить мегабиблиотеку?
• lz4, zstd, brotli, snappy, lzma… а то вдруг oodle
D.I.Y.
• Так эта! Как быстро/просто написать своё!?
• Глянуть данные, посчитать частоты
• Выдумать нехитрый (и лучше байтовый) код
• Суперчастые случаи – предельно коротко
• Редкие случаи – почти пофиг (!) насколько длинно
• Частое паковать, редкое раздувать
• На худой конец, возьмите varint / “mysql” / lsic
• Иногда (иногда!) случается чудо
• И быстрее, и компактнее (но таки дороже!!!)
Вопросы?
(Да, я всё ещё иногда читаю почту.)
shodan@sphinxsearch.com
Хочу все сжать / Андрей Аксенов (Sphinx)

More Related Content

Similar to Хочу все сжать / Андрей Аксенов (Sphinx)

Sphinx 3.0, поиск 15 лет спустя / Андрей Аксенов (Sphinx)
Sphinx 3.0, поиск 15 лет спустя / Андрей Аксенов (Sphinx)Sphinx 3.0, поиск 15 лет спустя / Андрей Аксенов (Sphinx)
Sphinx 3.0, поиск 15 лет спустя / Андрей Аксенов (Sphinx)
Ontico
 
Цена абстракции, Андрей Аксёнов (Sphinx)
Цена абстракции, Андрей Аксёнов (Sphinx)Цена абстракции, Андрей Аксёнов (Sphinx)
Цена абстракции, Андрей Аксёнов (Sphinx)Ontico
 
Почему оно не находится! / Андрей Аксенов (Sphinx)
Почему оно не находится! / Андрей Аксенов (Sphinx)Почему оно не находится! / Андрей Аксенов (Sphinx)
Почему оно не находится! / Андрей Аксенов (Sphinx)
Ontico
 
Низкоуровневые оптимизации. Андрей Аксенов. Unigine Open Air 2013
Низкоуровневые оптимизации. Андрей Аксенов. Unigine Open Air 2013Низкоуровневые оптимизации. Андрей Аксенов. Unigine Open Air 2013
Низкоуровневые оптимизации. Андрей Аксенов. Unigine Open Air 2013
Unigine Corp.
 
CodeFest 2012. Аксёнов А. — Как мы разрабатываем Sphinx
CodeFest 2012. Аксёнов А. — Как мы разрабатываем SphinxCodeFest 2012. Аксёнов А. — Как мы разрабатываем Sphinx
CodeFest 2012. Аксёнов А. — Как мы разрабатываем SphinxCodeFest
 
Как устроен поиск
Как устроен поискКак устроен поиск
Как устроен поискAndrew Aksyonoff
 
Как устроен поиск (Андрей Аксёнов)
Как устроен поиск (Андрей Аксёнов)Как устроен поиск (Андрей Аксёнов)
Как устроен поиск (Андрей Аксёнов)Ontico
 
Как устроен поиск / Андрей Аксенов (Sphinx)
Как устроен поиск / Андрей Аксенов (Sphinx)Как устроен поиск / Андрей Аксенов (Sphinx)
Как устроен поиск / Андрей Аксенов (Sphinx)
Ontico
 
Как устроена MySQL-репликация, Андрей Аксенов (Sphinx)
Как устроена MySQL-репликация, Андрей Аксенов (Sphinx)Как устроена MySQL-репликация, Андрей Аксенов (Sphinx)
Как устроена MySQL-репликация, Андрей Аксенов (Sphinx)Ontico
 
ОПК № 3 – Машинное представление целых чисел, символов, строк
ОПК № 3 – Машинное представление целых чисел, символов, строкОПК № 3 – Машинное представление целых чисел, символов, строк
ОПК № 3 – Машинное представление целых чисел, символов, строк
Vladimir Parfinenko
 
Крадущийся сервер, затаившийся диод (Андрей Аксенов)
Крадущийся сервер, затаившийся диод (Андрей Аксенов)Крадущийся сервер, затаившийся диод (Андрей Аксенов)
Крадущийся сервер, затаившийся диод (Андрей Аксенов)Ontico
 
Крадущийся сервер, затаившийся диод
Крадущийся сервер, затаившийся диодКрадущийся сервер, затаившийся диод
Крадущийся сервер, затаившийся диодAndrew Aksyonoff
 
С одним плюсом (Андрей Аксёнов)
С одним плюсом (Андрей Аксёнов)С одним плюсом (Андрей Аксёнов)
С одним плюсом (Андрей Аксёнов)
Unigine Corp.
 
Лекция 1. Начало.
Лекция 1. Начало.Лекция 1. Начало.
Лекция 1. Начало.
Roman Brovko
 
Low-level C/C++ Optimization by Anrew Axenov (Sphinx)
Low-level C/C++ Optimization by Anrew Axenov (Sphinx)Low-level C/C++ Optimization by Anrew Axenov (Sphinx)
Low-level C/C++ Optimization by Anrew Axenov (Sphinx)Vadim Kosov
 
Низкоуровневая Оптимизация (Андрей Аксенов)
Низкоуровневая Оптимизация (Андрей Аксенов)Низкоуровневая Оптимизация (Андрей Аксенов)
Низкоуровневая Оптимизация (Андрей Аксенов)Ontico
 
2014-11-01 02 Иван Зезюля. To go или не to go
2014-11-01 02 Иван Зезюля. To go или не to go2014-11-01 02 Иван Зезюля. To go или не to go
2014-11-01 02 Иван Зезюля. To go или не to go
Омские ИТ-субботники
 
Как устроен NoSQL, Андрей Аксенов (Sphinx)
Как устроен NoSQL, Андрей Аксенов (Sphinx)Как устроен NoSQL, Андрей Аксенов (Sphinx)
Как устроен NoSQL, Андрей Аксенов (Sphinx)Ontico
 
А. Аксенов "Как устроен NoSql", DUMP-2014
А. Аксенов "Как устроен NoSql", DUMP-2014А. Аксенов "Как устроен NoSql", DUMP-2014
А. Аксенов "Как устроен NoSql", DUMP-2014it-people
 

Similar to Хочу все сжать / Андрей Аксенов (Sphinx) (20)

Sphinx 3.0, поиск 15 лет спустя / Андрей Аксенов (Sphinx)
Sphinx 3.0, поиск 15 лет спустя / Андрей Аксенов (Sphinx)Sphinx 3.0, поиск 15 лет спустя / Андрей Аксенов (Sphinx)
Sphinx 3.0, поиск 15 лет спустя / Андрей Аксенов (Sphinx)
 
Цена абстракции, Андрей Аксёнов (Sphinx)
Цена абстракции, Андрей Аксёнов (Sphinx)Цена абстракции, Андрей Аксёнов (Sphinx)
Цена абстракции, Андрей Аксёнов (Sphinx)
 
Почему оно не находится! / Андрей Аксенов (Sphinx)
Почему оно не находится! / Андрей Аксенов (Sphinx)Почему оно не находится! / Андрей Аксенов (Sphinx)
Почему оно не находится! / Андрей Аксенов (Sphinx)
 
Низкоуровневые оптимизации. Андрей Аксенов. Unigine Open Air 2013
Низкоуровневые оптимизации. Андрей Аксенов. Unigine Open Air 2013Низкоуровневые оптимизации. Андрей Аксенов. Unigine Open Air 2013
Низкоуровневые оптимизации. Андрей Аксенов. Unigine Open Air 2013
 
CodeFest 2012. Аксёнов А. — Как мы разрабатываем Sphinx
CodeFest 2012. Аксёнов А. — Как мы разрабатываем SphinxCodeFest 2012. Аксёнов А. — Как мы разрабатываем Sphinx
CodeFest 2012. Аксёнов А. — Как мы разрабатываем Sphinx
 
Как устроен поиск
Как устроен поискКак устроен поиск
Как устроен поиск
 
Как устроен поиск (Андрей Аксёнов)
Как устроен поиск (Андрей Аксёнов)Как устроен поиск (Андрей Аксёнов)
Как устроен поиск (Андрей Аксёнов)
 
Как устроен поиск / Андрей Аксенов (Sphinx)
Как устроен поиск / Андрей Аксенов (Sphinx)Как устроен поиск / Андрей Аксенов (Sphinx)
Как устроен поиск / Андрей Аксенов (Sphinx)
 
Как устроена MySQL-репликация, Андрей Аксенов (Sphinx)
Как устроена MySQL-репликация, Андрей Аксенов (Sphinx)Как устроена MySQL-репликация, Андрей Аксенов (Sphinx)
Как устроена MySQL-репликация, Андрей Аксенов (Sphinx)
 
ОПК № 3 – Машинное представление целых чисел, символов, строк
ОПК № 3 – Машинное представление целых чисел, символов, строкОПК № 3 – Машинное представление целых чисел, символов, строк
ОПК № 3 – Машинное представление целых чисел, символов, строк
 
Крадущийся сервер, затаившийся диод (Андрей Аксенов)
Крадущийся сервер, затаившийся диод (Андрей Аксенов)Крадущийся сервер, затаившийся диод (Андрей Аксенов)
Крадущийся сервер, затаившийся диод (Андрей Аксенов)
 
Крадущийся сервер, затаившийся диод
Крадущийся сервер, затаившийся диодКрадущийся сервер, затаившийся диод
Крадущийся сервер, затаившийся диод
 
С одним плюсом (Андрей Аксёнов)
С одним плюсом (Андрей Аксёнов)С одним плюсом (Андрей Аксёнов)
С одним плюсом (Андрей Аксёнов)
 
Лекция 1. Начало.
Лекция 1. Начало.Лекция 1. Начало.
Лекция 1. Начало.
 
Low-level C/C++ Optimization by Anrew Axenov (Sphinx)
Low-level C/C++ Optimization by Anrew Axenov (Sphinx)Low-level C/C++ Optimization by Anrew Axenov (Sphinx)
Low-level C/C++ Optimization by Anrew Axenov (Sphinx)
 
Низкоуровневая Оптимизация (Андрей Аксенов)
Низкоуровневая Оптимизация (Андрей Аксенов)Низкоуровневая Оптимизация (Андрей Аксенов)
Низкоуровневая Оптимизация (Андрей Аксенов)
 
Cpp
CppCpp
Cpp
 
2014-11-01 02 Иван Зезюля. To go или не to go
2014-11-01 02 Иван Зезюля. To go или не to go2014-11-01 02 Иван Зезюля. To go или не to go
2014-11-01 02 Иван Зезюля. To go или не to go
 
Как устроен NoSQL, Андрей Аксенов (Sphinx)
Как устроен NoSQL, Андрей Аксенов (Sphinx)Как устроен NoSQL, Андрей Аксенов (Sphinx)
Как устроен NoSQL, Андрей Аксенов (Sphinx)
 
А. Аксенов "Как устроен NoSql", DUMP-2014
А. Аксенов "Как устроен NoSql", DUMP-2014А. Аксенов "Как устроен NoSql", DUMP-2014
А. Аксенов "Как устроен NoSql", DUMP-2014
 

More from Ontico

One-cloud — система управления дата-центром в Одноклассниках / Олег Анастасье...
One-cloud — система управления дата-центром в Одноклассниках / Олег Анастасье...One-cloud — система управления дата-центром в Одноклассниках / Олег Анастасье...
One-cloud — система управления дата-центром в Одноклассниках / Олег Анастасье...
Ontico
 
Масштабируя DNS / Артем Гавриченков (Qrator Labs)
Масштабируя DNS / Артем Гавриченков (Qrator Labs)Масштабируя DNS / Артем Гавриченков (Qrator Labs)
Масштабируя DNS / Артем Гавриченков (Qrator Labs)
Ontico
 
Создание BigData-платформы для ФГУП Почта России / Андрей Бащенко (Luxoft)
Создание BigData-платформы для ФГУП Почта России / Андрей Бащенко (Luxoft)Создание BigData-платформы для ФГУП Почта России / Андрей Бащенко (Luxoft)
Создание BigData-платформы для ФГУП Почта России / Андрей Бащенко (Luxoft)
Ontico
 
Готовим тестовое окружение, или сколько тестовых инстансов вам нужно / Алекса...
Готовим тестовое окружение, или сколько тестовых инстансов вам нужно / Алекса...Готовим тестовое окружение, или сколько тестовых инстансов вам нужно / Алекса...
Готовим тестовое окружение, или сколько тестовых инстансов вам нужно / Алекса...
Ontico
 
Новые технологии репликации данных в PostgreSQL / Александр Алексеев (Postgre...
Новые технологии репликации данных в PostgreSQL / Александр Алексеев (Postgre...Новые технологии репликации данных в PostgreSQL / Александр Алексеев (Postgre...
Новые технологии репликации данных в PostgreSQL / Александр Алексеев (Postgre...
Ontico
 
PostgreSQL Configuration for Humans / Alvaro Hernandez (OnGres)
PostgreSQL Configuration for Humans / Alvaro Hernandez (OnGres)PostgreSQL Configuration for Humans / Alvaro Hernandez (OnGres)
PostgreSQL Configuration for Humans / Alvaro Hernandez (OnGres)
Ontico
 
Inexpensive Datamasking for MySQL with ProxySQL — Data Anonymization for Deve...
Inexpensive Datamasking for MySQL with ProxySQL — Data Anonymization for Deve...Inexpensive Datamasking for MySQL with ProxySQL — Data Anonymization for Deve...
Inexpensive Datamasking for MySQL with ProxySQL — Data Anonymization for Deve...
Ontico
 
Опыт разработки модуля межсетевого экранирования для MySQL / Олег Брославский...
Опыт разработки модуля межсетевого экранирования для MySQL / Олег Брославский...Опыт разработки модуля межсетевого экранирования для MySQL / Олег Брославский...
Опыт разработки модуля межсетевого экранирования для MySQL / Олег Брославский...
Ontico
 
ProxySQL Use Case Scenarios / Alkin Tezuysal (Percona)
ProxySQL Use Case Scenarios / Alkin Tezuysal (Percona)ProxySQL Use Case Scenarios / Alkin Tezuysal (Percona)
ProxySQL Use Case Scenarios / Alkin Tezuysal (Percona)
Ontico
 
MySQL Replication — Advanced Features / Петр Зайцев (Percona)
MySQL Replication — Advanced Features / Петр Зайцев (Percona)MySQL Replication — Advanced Features / Петр Зайцев (Percona)
MySQL Replication — Advanced Features / Петр Зайцев (Percona)
Ontico
 
Внутренний open-source. Как разрабатывать мобильное приложение большим количе...
Внутренний open-source. Как разрабатывать мобильное приложение большим количе...Внутренний open-source. Как разрабатывать мобильное приложение большим количе...
Внутренний open-source. Как разрабатывать мобильное приложение большим количе...
Ontico
 
Подробно о том, как Causal Consistency реализовано в MongoDB / Михаил Тюленев...
Подробно о том, как Causal Consistency реализовано в MongoDB / Михаил Тюленев...Подробно о том, как Causal Consistency реализовано в MongoDB / Михаил Тюленев...
Подробно о том, как Causal Consistency реализовано в MongoDB / Михаил Тюленев...
Ontico
 
Балансировка на скорости проводов. Без ASIC, без ограничений. Решения NFWare ...
Балансировка на скорости проводов. Без ASIC, без ограничений. Решения NFWare ...Балансировка на скорости проводов. Без ASIC, без ограничений. Решения NFWare ...
Балансировка на скорости проводов. Без ASIC, без ограничений. Решения NFWare ...
Ontico
 
Перехват трафика — мифы и реальность / Евгений Усков (Qrator Labs)
Перехват трафика — мифы и реальность / Евгений Усков (Qrator Labs)Перехват трафика — мифы и реальность / Евгений Усков (Qrator Labs)
Перехват трафика — мифы и реальность / Евгений Усков (Qrator Labs)
Ontico
 
И тогда наверняка вдруг запляшут облака! / Алексей Сушков (ПЕТЕР-СЕРВИС)
И тогда наверняка вдруг запляшут облака! / Алексей Сушков (ПЕТЕР-СЕРВИС)И тогда наверняка вдруг запляшут облака! / Алексей Сушков (ПЕТЕР-СЕРВИС)
И тогда наверняка вдруг запляшут облака! / Алексей Сушков (ПЕТЕР-СЕРВИС)
Ontico
 
Как мы заставили Druid работать в Одноклассниках / Юрий Невиницин (OK.RU)
Как мы заставили Druid работать в Одноклассниках / Юрий Невиницин (OK.RU)Как мы заставили Druid работать в Одноклассниках / Юрий Невиницин (OK.RU)
Как мы заставили Druid работать в Одноклассниках / Юрий Невиницин (OK.RU)
Ontico
 
Разгоняем ASP.NET Core / Илья Вербицкий (WebStoating s.r.o.)
Разгоняем ASP.NET Core / Илья Вербицкий (WebStoating s.r.o.)Разгоняем ASP.NET Core / Илья Вербицкий (WebStoating s.r.o.)
Разгоняем ASP.NET Core / Илья Вербицкий (WebStoating s.r.o.)
Ontico
 
100500 способов кэширования в Oracle Database или как достичь максимальной ск...
100500 способов кэширования в Oracle Database или как достичь максимальной ск...100500 способов кэширования в Oracle Database или как достичь максимальной ск...
100500 способов кэширования в Oracle Database или как достичь максимальной ск...
Ontico
 
Apache Ignite Persistence: зачем Persistence для In-Memory, и как он работает...
Apache Ignite Persistence: зачем Persistence для In-Memory, и как он работает...Apache Ignite Persistence: зачем Persistence для In-Memory, и как он работает...
Apache Ignite Persistence: зачем Persistence для In-Memory, и как он работает...
Ontico
 
Механизмы мониторинга баз данных: взгляд изнутри / Дмитрий Еманов (Firebird P...
Механизмы мониторинга баз данных: взгляд изнутри / Дмитрий Еманов (Firebird P...Механизмы мониторинга баз данных: взгляд изнутри / Дмитрий Еманов (Firebird P...
Механизмы мониторинга баз данных: взгляд изнутри / Дмитрий Еманов (Firebird P...
Ontico
 

More from Ontico (20)

One-cloud — система управления дата-центром в Одноклассниках / Олег Анастасье...
One-cloud — система управления дата-центром в Одноклассниках / Олег Анастасье...One-cloud — система управления дата-центром в Одноклассниках / Олег Анастасье...
One-cloud — система управления дата-центром в Одноклассниках / Олег Анастасье...
 
Масштабируя DNS / Артем Гавриченков (Qrator Labs)
Масштабируя DNS / Артем Гавриченков (Qrator Labs)Масштабируя DNS / Артем Гавриченков (Qrator Labs)
Масштабируя DNS / Артем Гавриченков (Qrator Labs)
 
Создание BigData-платформы для ФГУП Почта России / Андрей Бащенко (Luxoft)
Создание BigData-платформы для ФГУП Почта России / Андрей Бащенко (Luxoft)Создание BigData-платформы для ФГУП Почта России / Андрей Бащенко (Luxoft)
Создание BigData-платформы для ФГУП Почта России / Андрей Бащенко (Luxoft)
 
Готовим тестовое окружение, или сколько тестовых инстансов вам нужно / Алекса...
Готовим тестовое окружение, или сколько тестовых инстансов вам нужно / Алекса...Готовим тестовое окружение, или сколько тестовых инстансов вам нужно / Алекса...
Готовим тестовое окружение, или сколько тестовых инстансов вам нужно / Алекса...
 
Новые технологии репликации данных в PostgreSQL / Александр Алексеев (Postgre...
Новые технологии репликации данных в PostgreSQL / Александр Алексеев (Postgre...Новые технологии репликации данных в PostgreSQL / Александр Алексеев (Postgre...
Новые технологии репликации данных в PostgreSQL / Александр Алексеев (Postgre...
 
PostgreSQL Configuration for Humans / Alvaro Hernandez (OnGres)
PostgreSQL Configuration for Humans / Alvaro Hernandez (OnGres)PostgreSQL Configuration for Humans / Alvaro Hernandez (OnGres)
PostgreSQL Configuration for Humans / Alvaro Hernandez (OnGres)
 
Inexpensive Datamasking for MySQL with ProxySQL — Data Anonymization for Deve...
Inexpensive Datamasking for MySQL with ProxySQL — Data Anonymization for Deve...Inexpensive Datamasking for MySQL with ProxySQL — Data Anonymization for Deve...
Inexpensive Datamasking for MySQL with ProxySQL — Data Anonymization for Deve...
 
Опыт разработки модуля межсетевого экранирования для MySQL / Олег Брославский...
Опыт разработки модуля межсетевого экранирования для MySQL / Олег Брославский...Опыт разработки модуля межсетевого экранирования для MySQL / Олег Брославский...
Опыт разработки модуля межсетевого экранирования для MySQL / Олег Брославский...
 
ProxySQL Use Case Scenarios / Alkin Tezuysal (Percona)
ProxySQL Use Case Scenarios / Alkin Tezuysal (Percona)ProxySQL Use Case Scenarios / Alkin Tezuysal (Percona)
ProxySQL Use Case Scenarios / Alkin Tezuysal (Percona)
 
MySQL Replication — Advanced Features / Петр Зайцев (Percona)
MySQL Replication — Advanced Features / Петр Зайцев (Percona)MySQL Replication — Advanced Features / Петр Зайцев (Percona)
MySQL Replication — Advanced Features / Петр Зайцев (Percona)
 
Внутренний open-source. Как разрабатывать мобильное приложение большим количе...
Внутренний open-source. Как разрабатывать мобильное приложение большим количе...Внутренний open-source. Как разрабатывать мобильное приложение большим количе...
Внутренний open-source. Как разрабатывать мобильное приложение большим количе...
 
Подробно о том, как Causal Consistency реализовано в MongoDB / Михаил Тюленев...
Подробно о том, как Causal Consistency реализовано в MongoDB / Михаил Тюленев...Подробно о том, как Causal Consistency реализовано в MongoDB / Михаил Тюленев...
Подробно о том, как Causal Consistency реализовано в MongoDB / Михаил Тюленев...
 
Балансировка на скорости проводов. Без ASIC, без ограничений. Решения NFWare ...
Балансировка на скорости проводов. Без ASIC, без ограничений. Решения NFWare ...Балансировка на скорости проводов. Без ASIC, без ограничений. Решения NFWare ...
Балансировка на скорости проводов. Без ASIC, без ограничений. Решения NFWare ...
 
Перехват трафика — мифы и реальность / Евгений Усков (Qrator Labs)
Перехват трафика — мифы и реальность / Евгений Усков (Qrator Labs)Перехват трафика — мифы и реальность / Евгений Усков (Qrator Labs)
Перехват трафика — мифы и реальность / Евгений Усков (Qrator Labs)
 
И тогда наверняка вдруг запляшут облака! / Алексей Сушков (ПЕТЕР-СЕРВИС)
И тогда наверняка вдруг запляшут облака! / Алексей Сушков (ПЕТЕР-СЕРВИС)И тогда наверняка вдруг запляшут облака! / Алексей Сушков (ПЕТЕР-СЕРВИС)
И тогда наверняка вдруг запляшут облака! / Алексей Сушков (ПЕТЕР-СЕРВИС)
 
Как мы заставили Druid работать в Одноклассниках / Юрий Невиницин (OK.RU)
Как мы заставили Druid работать в Одноклассниках / Юрий Невиницин (OK.RU)Как мы заставили Druid работать в Одноклассниках / Юрий Невиницин (OK.RU)
Как мы заставили Druid работать в Одноклассниках / Юрий Невиницин (OK.RU)
 
Разгоняем ASP.NET Core / Илья Вербицкий (WebStoating s.r.o.)
Разгоняем ASP.NET Core / Илья Вербицкий (WebStoating s.r.o.)Разгоняем ASP.NET Core / Илья Вербицкий (WebStoating s.r.o.)
Разгоняем ASP.NET Core / Илья Вербицкий (WebStoating s.r.o.)
 
100500 способов кэширования в Oracle Database или как достичь максимальной ск...
100500 способов кэширования в Oracle Database или как достичь максимальной ск...100500 способов кэширования в Oracle Database или как достичь максимальной ск...
100500 способов кэширования в Oracle Database или как достичь максимальной ск...
 
Apache Ignite Persistence: зачем Persistence для In-Memory, и как он работает...
Apache Ignite Persistence: зачем Persistence для In-Memory, и как он работает...Apache Ignite Persistence: зачем Persistence для In-Memory, и как он работает...
Apache Ignite Persistence: зачем Persistence для In-Memory, и как он работает...
 
Механизмы мониторинга баз данных: взгляд изнутри / Дмитрий Еманов (Firebird P...
Механизмы мониторинга баз данных: взгляд изнутри / Дмитрий Еманов (Firebird P...Механизмы мониторинга баз данных: взгляд изнутри / Дмитрий Еманов (Firebird P...
Механизмы мониторинга баз данных: взгляд изнутри / Дмитрий Еманов (Firebird P...
 

Хочу все сжать / Андрей Аксенов (Sphinx)

  • 1. Хочу всё сжать Андрей Аксёнов v.1.1 // highload 2017
  • 2. disclaimer // 18+ // [R] // уберите детей • Да, как обычно обзорный доклад, без хардкора • Упомяну 10-20 методов вскользь, дальше в википедию • Чуть в детали в 5-7 простых методов, дальше в википедию • Ну или в коридор (лучше), время до обеда есть, гуляем • Да, репетировал; нет, в бюджет времени не уложусь • Speaker rant, доклады не в том порядке!!!
  • 3. Про сжатие • Несколько шире архиваторов и даже zlib • 7zip, rar, tar.bz2 – сжатие • jpeg, mp3, aac, h264, bpg – сжатие • gzip, lz4, snappy, brotli, zstd – сжатие • huffman, ari/range, varint – сжатие • byte x = byte(float(y)*127) – тоже сжатие!!! • Бывает таки не только в мире C/C++ • Java, Go, C#, Rust? Python, JavaScript, PHP? • Причём тут вообще “highload”?...
  • 4. Сжатие == две ключевых части • Как мне идеально сжать что_угодно? 1. Хитро преобразовать (или нет) 2. Ловко закодировать (или нет) • Зачем преобразовывать? • Чтобы ловчее кодировалось! • Кодировать это уже небось адски сложно? • Шокирующие новости – НЕТ! • И, собственно, код – фундамент успеха • В отличие от IT и даже программирования, kekeke
  • 5. Любой код за 1 минуту • Buy low, sell high? • Круглое носить, квадратное катать? • Частое паковать, редкое раздувать • Причем, речь всегда о числах (и битах/байтах) • Классика: Huffman, Golomb, Rice, Elias, ari/range • Поновей: varint, S9/S16, PFOR • Важно: свой личный DIY нередко зажжот
  • 6. Что значит “частое”? • Например, английские тексты, pg.txt • Буквы a-z ожидаемо чаще всех других • 336.2m всего • 242.0m [a-z] • 31.0m [e] • 21.9m [t] • 19.4m [a] • … • 8.1m [A-Z]
  • 7. Что значит “частое”? • Например, английские тексты, pg.txt • Байты a-z тоже сильно чаще всех других • 336.2m всего • 242.0m [a-z] • 54.1m [ ] самый частый байт (да, пробел) • 31.0m [e] • …
  • 8. Наш первый (тупой) код • Пробел = 1 бит, “0”, и так 54.1m раз • Остальное = 9 бит, “1 vvvvvvvv”, 282.1m раз • -378.7 mbit = -7 bit * 54.1m • +282.1 mbit = +1 bit * 282.1m • -12.0 mbyte = -96.6 = -378.7+282.1 mbit • Наш первый профит – ура!!! • Уже плюс сжатие, но всего на 3.6% :( • Минус случайный доступ, “плюс” бииииты
  • 9. Почему так плохо?! • Мало сэкономили, достаточно проиграли • Надо экономить на N частых байтах, а не 1 • Давайте занумеруем байты по частоте • 1 = ‘ ‘ • 2 = ‘e’ • 3 = ‘t’ • 4 = ‘a’ • … • Надо передать таблицу = 256 байт, фигня
  • 10. Наш второй код • Давайте номер #1 кодировать в 1 бит “1” • Давайте остальные номера кодировать • Минимальным нужным числом бит B • Которое кодировать цепочкой (B-1) нулей • #1 = “1” 1 бит ‘ ‘ • #2 = “0 10” 3 бита ‘e’ • #3 = “0 11” 3 бита ‘t’ • #4 = “00 100” 5 бит ‘a’
  • 11. Не совсем наш код • Результаты кодирования? • #1, 8 => 1 бит • #2-3, 8 => 3 бита • #4-7, 8 => 5 бит • #8-15, 8 => 7 бит • #16-256, 8 => 9, 11, …, 17 бит • Тем не менее, 336.2mb => 231.0mb, 68.7% • Это, кстати, Elias gamma code [Elias1975] • Но, конечно, бииииты
  • 12. Наш третий код • Давайте #1-8 кодировать в 4 бита, “1 vvv” • Давайте #9-135 в 8 бит, “0 ddd dddd”, d = v-9 • Давайте #136-256 в 16 бит, “0 111 1111” V • ref 336.2mb 100.0% • v2-gamma 231.0mb 68.7% • v3-nibble 232.0mb 69.0% • Чёрт, чуток проиграли дедушке Elias • Но, зато больше нету битов! (побитного чтения)
  • 13. Наш третий код • Кстати, а если #136-256 ваще в 256 бит?! • v3-nibble 232.0mb, 69.0% • v3-nibble-megaloss 232.4mb, 69.1%
  • 14. Наш третий код • Кстати, а если #136-256 ваще в 256 бит?! • v3-nibble 232.0mb, 69.0% • v3-nibble-megaloss 232.4mb, 69.1%
  • 15. Наш третий код – важное замечание • Кстати, а если #136-256 ваще в 256 бит?! • v3-nibble 232.0mb, 69.0% • v3-nibble-megaloss 232.4mb, 69.1% • Так будет НЕ ВСЕГДА • Но здесь – вот такая статистика • И – не только здесь
  • 16. Почему побитно – отстой? int get_byte() { return m_buf[m_pos++]; }
  • 17. Почему побитно – отстой? int get_nibble() { return (m_pos & 1) ? m_buf[m_pos++ >> 1] & 15 : (m_buf[m_pos++ >> 1] >> 4); }
  • 18. Почему побитно – отстой? int get_bits(int n) { static const byte mask[8] = {1,3,7,15,31,63,127,255}; int r = 0, s = 0, pmax = m_pos + n; while (m_pos < pmax) { int b = (m_pos & 7); int got = min(pmax – m_pos, 8 – pbit); r += ((m_buf[m_pos >> 3] >> b) & mask[got]) << s; m_pos += got; s += got; } return r; } // probably buggy like hell too
  • 19. Почему побитно – отстой? int get_bits(int n) { int64_t v = *(int64_t*)(m_buf + (m_pos >> 3)); v >>= (m_pos & 7); m_pos += n; return int(v & ((1ULL << n) – 1)); } // needs a post-buffer gap, of course
  • 20. Почему побитно – отстой? int get_bits(int n) { int64_t v = *(int64_t*)(m_buf + (m_pos >> 3)); v >>= (m_pos & 7); m_pos += n; return int(v & ((1ULL << n) – 1)); } // ...vs... int get_byte() { return *m_ptr++; }
  • 21. А можно ли тогда – байтами?! • Можно!!! • Тут – не выйдет, т.к. байты и кодируем • Лежит хомяк, на нём хомяк… • Там – выйдет, где числа побольше • Например, произвольные int, до 32 бит • Например, длины строк • Например, строки-то часто, кхм, недлинные
  • 22. Наш четвертый код • Кодируем произвольную длину, 4 байта • Кодируем по 1 байту за раз, “M VVV VVVV” • Верхний 1 бит, m == 0 конец, 1 не конец • Остальные 7 бит, vvvvvvv == кусок значения • Длины 0-127 == 1 байт • Длины 128-16383 == 2 байта, и т.д. • Длины 268 435 456+ == 5 байт, конечно • Это, кстати, VarInt (всенародно любимый)
  • 23. За что его любить!? int decode_varint() { int r = *m_ptr & 127; while (*m_ptr++ & 128) r = (r << 7) + (*m_ptr & 127); return r; } // btw, 500+ mb/sec, 5+ yr/ago; also, JS
  • 24. Наш пятый код • Кодируем любой честный int (напр. длина) • Значения 0-251 кладем как есть == 1 байт • Иначе 252-255 == “ща поедет 1-4 байта” • 173 => {173} • 253 => {252, 253} • 0xABCD => {253, 0xCD, 0xAB} // native order • Длины 252+ == 2-5 байт, всегда +1 лишний • Это, кстати, MySQL (wire protocol, at least)
  • 25. Давай уж опять свой декодер int decode_mysql() { int *p = (int*)++m_ptr; switch (m_ptr[-1]) { case 252: return *p & 0xff; case 253: return *p & 0xffff; case 254: return *p & 0xffffff; case 255: return *p; default: return m_ptr[-1]; } // did not bench, but fast
  • 26. Мне скучно и я пожалуй пойду • Стоять, мы уже вообще научились кодировать • Ну, почти • Конечно, есть еще 10+ методов, и 2-3+ крутых (GVI, S9, PFOR…) • Но для начала хватит и этих, и они вас не подведут • Но таки давайте чуть про “преобразовывать” • Обзорно, без исходников уже • Ну, почти
  • 27. Дискотека 1970х • А вот у меня pg.txt, там текст • [Gutenberg] в позициях 11, 307 итд в pg.txt • Давайте в позиции 11 сохраним сами байты • cmd = 0 (literal), len = 9, data = [Gutenberg] => +1 byte
  • 28. Дискотека 1970х • А вот у меня pg.txt, там текст • [Gutenberg] в позициях 11, 307 итд в pg.txt • Давайте в позиции 11 сохраним сами байты • cmd = 0 (literal), len = 9, data = [Gutenberg] => +1 byte • Давайте в позиции 307 сохраним ссылку • cmd = 1 (match), len = 9, data = 296 => -6 bytes
  • 29. Дискотека 1970х • А вот у меня pg.txt, там текст • [Gutenberg] в позициях 11, 307 итд в pg.txt • Давайте в позиции 11 сохраним сами байты • cmd = 0 (literal), len = 9, data = [Gutenberg] => +1 byte • Давайте в позиции 307 сохраним ссылку • cmd = 1 (match), len = 9, data = 296 => -6 bytes • Все циферки как-то кодируем, мы ж умеем • Например ((len << 1) + command) вместе! чтобы +1, а не +2 там вверху • Ура, мы изобрели семейство LZ (Lempel-Ziv)
  • 30. Дискотека 1980х • А вот у меня pg.txt, там текст • Увидели в потоке байт X, а далее увидели Y • Вероятность увидеть именно Y зависит от X • “ВЕ” чаще, “ВЬ” реже, “ЕЬ” вообще нету в языке • Давайте сделаем 256 таблиц по 256 счетчиков
  • 31. Почему так плохо?! • Мало сэкономили, достаточно проиграли • Надо экономить на N частых байтах, а не 1 • Давайте занумеруем байты по частоте • 1 = ‘ ‘ • 2 = ‘e’ • 3 = ‘t’ • 4 = ‘a’ • … • Надо передать таблицу = 256 байт, фигня
  • 32. Дискотека 1980х • А вот у меня pg.txt, там текст • Увидели в потоке байт X, а далее увидели Y • Вероятность увидеть именно Y зависит от X • “ВЕ” чаще, “ВЬ” реже, “ЕЬ” вообще нету в языке • Давайте сделаем 256 таблиц по 256 счетчиков • Давайте обновлять их на лету (чтобы 1 проход) • Давайте вместо Y кодировать номер Y в table[X] • Ура, мы изобрели простейший PPM(1) • А если контекст X это N байт, то PPM(N)
  • 33. Дискотека 1990х, 2000х • Продвижения кое-какие были, да • Внимание не заостряем, нет – времени нет :P • Считаем, что в святые 90е реализовали и отполировали
  • 34. Дискотека 2010х • А вот у меня pg.txt, там текст • Давайте заведем словарь!!! • Увидели слово [project], закодировали номер в словаре • Увидели незнакомое, закодировали “как в LZ” • Ну нет, не совсем как LZ, ладно, вру • Ещё “модель контекста” какая-то, непонятно, в вики глянем • Ура, мы изобрели Brotli • А пока мы его изобретали, уже полвеба умеет (якобы)
  • 35. Как же “преобразовывать”? • Внезапно, теперь мы можем всё!!! • Gzip = LZ77 + Huffman • 7zip = LZMA / PPM + range coder (грубо говоря) • Lz4 = LZ77 + DIY_code • byte marker = (literal_len << 4) + match_len • byte literal [ literal_len ] • short offset // от текущей позиции назад • Zstd = LZ77 + tANS entropy coder • Brotli = LZ77 + Huffman + context modeling + dict • …
  • 36. А если там не текст? • А у меня поиск, а там (удивление!) не текст! • А там слово “hello” => список номеров • {12, 31, 79, 139, 517, …, 1234567, 1234568, 1234569, …} • Номера всегда растут • Тупо жать каждый номер? Нет! • Тупо жать дельты между номерами? Да!!! • {12, 19, 48, 60, 378, …, ?, 1, 1, …} – или даже • {11, 18, 47, 59, 377, …, ?, 0, 0, …} • Ура, мы изобрели Lucene, Sphinx, Google... 
  • 37. Нельзя ли еще лучше? • А что, если там “стопслово”? • docs {1,2,3,5,6,9,10,11,12,…} x 10M+ раз • deltas {1,1,2,2,1,3,1,1,1,…} • Даже по 1 байту = 8 бит на дельту чота жырно • А давайте группы по 32 дельты? • byte nbits; uint deltas[nbits]; • 32x{1} – было 32 байта, стало 1 (!!!) байт • 32x{1,2,3,4} – было 32, стало 9 байт • Ура, мы изобрели типа FOR (Frame Of Reference)
  • 38. Обратите внимание, локальный K • Оригинал • 32 docid * 4 / 8 bytes == 128 / 256 bytes • small-deltas + varint => 32 bytes, k=0.25 / 0.12 • small-deltas + FOR => 9 bytes, k=0.07 / 0.035 • big-deltas так просто не сдаются, конечно • Поэтому общий результат всегда хуже 
  • 39. Но совсем хорошо – совсем тупо! • Пусть max(doc_id) = M – и авось M << 2^32 • Тупо строим bitmap на M бит и всё • А ещё такие битмапы дико быстро пересекать • Работает НЕ всегда! • Пусть слово матчит N документов, типа 3 шт, на M = 10 млн бит • При N < M/32 будет лучше даже vector<int> без сжатия!!! • При N < M/8 лучше (?) vector<byte> deltas • Тупо, но работает, чё (ещё и лучше всех) • k=0.031 / 0.016, кстати
  • 40. Как ещё “преобразовывать”? • Пажжи, а если у меня не текст и не поиск?! • А вот у меня лог (BIGINT uid, BIGINT tstamp) • На самом деле колонок больше, но пусть так • Точность до микросекунды, потому bigint • Пользователей мало, но мы амбициозны • Не, tstamp растет (разумеется) • А далее userid типа рандомный • Всё, расходимся, такое не сжать?
  • 41. Сжимаем “несжимаемое” • Долбим (uid, tstamp) на блоки • Лучше побольше, 128+ записей • В каждом блоке пробуем • RLE == N повторов значения X => {X, N} • MinDelta == из всех uid вычитаем min(uid) • BlockDict == если мало разных значений, стром LUT • DeltaRange == вместо значений кодируем дельты • CommonDelta == словарь дельт и entropy coder • …
  • 42. Сжимаем “несжимаемое” • Растущие tstamp => вчистую DeltaRange • “Случайные” uid, ip и т.п. => ну, зависит • Если чутка неслучайные, что-то сработает • На крайняк, тупо урезать uid до 5-6 байт :P • Залетные неслучайные city_id, price, ... => ужмутся в разы, до единиц бит, см. K • Ура, мы изобрели Vertica • И возможно ClickHouse (но это не точно) • Но Миловидов пока не опроверг!!!
  • 43. Я вообще может по ML/DS угораю • Нейросетка double[512] насчитала • Памяти в мобиле ваще нет • Все остальные трюки не работают норм • Округление == тоже трансформ? Или код? • short[512] ровно в 4 раза меньше!!! • Тупо, но работает, чё • Гусары, ни слова про PCA
  • 44. Я ничего никогда не храню!!! • Но переменные-то в программе бывают? struct Token { int start, len, pos; bool is_stop; int multi_pos_len; // mostly 0 } // sizeof(Token) == 20
  • 45. True magic story, bro • Байка из Sphinx, ага (про snippets) • Раз, парсим текст – что мееедленно • Два, обходим токены – ищем лучшие спаны • Три, обходим токены – генерируем ответ • Первая реализация, vector<Token> • Плюс токены были ещё толще, 40+ байт • С документами в 10 kb все работало отлично • Но тут прилетел документ в 226000 kb • Сожрал 4+ gb памяти и упал 
  • 46. True magic story, bro • Вторая реализация, жатый поток • Раз, парсим – и тут же жмем • Два, обходим – разжимая “по одному” • Три, опять обходим – опять разжимая • Ожидания? • ref жрал, выходит, до ~20x doc_size • медленно, хотелось уложиться в 2x от ref • компактно, но чтобы ~1x doc_size => на мелких документах был план отключить
  • 47. True magic story, bro • Реальность? • компактнее, чем ожидалось (0.3…0.5 doc_size) • всегда быстрее, чем ref (чуть не 2x иногда) • отлично, 1 вариант кода, а не 2 варианта • В чём обещанная МАГИЯ? • Быстро // дешево // хорошо, да?
  • 48. “Треугольник” Выбора – в жизни, без магии БЫСТРО ДЕШЕВО ХОРОШО МЫ
  • 49. Да я вообще не программист!!! • Программы хоть запускать умеешь?! • Вчера были gzip, bzip2 – и всё • Сегодня есть lz4, snappy, brotli, zstd, 7z… • Сегодня нужно выбирать – всегда есть чего • Особенно если ты всё же программист • Что важно – есть кодеки “на скорости RAM” • Втыкаются везде, см. MySQL, Postgres, …
  • 50. pg.txt, 336.2 mb, win7-ymmv! Size (mb) Pack (sec) Unp (sec) v3-nibble 232.0 - - gzip -1 149.8 7.7 3.4 gzip -9 125.8 28.8 3.0 bzip2 92.8 44.1 29.2 p7z (*) 88.8 109.4 3.8 zstd -3 119.6 3.0 0.8 zstd -9 107.8 16.8 0.8 Size (mb) Pack (sec) Unp (sec) brotli -1 147.7 4.7 2.5 brotli -3 124.2 8.7 1.9 brotli -9 102.9 106.0 1.6 lz4 -1 198.0 0.36 0.23 lz4 -9 145.0 4.5 0.22 …
  • 51. Зачем вообще был этот доклад?!?!• “Все” типа умеют в vector/list/hash • Уметь надо во много больше • Умеют немногие – а что изучать? • Сжатие может не понадобиться никогда • Сжатие может сильно помочь – см. магия • Сложного там ничего – см. доклад • Плюс актуальные ключевики – вот они:
  • 52. keywords • Как ловко кодировать какие-то числа? • Elias, Rice, Golomb, Huffman, arithmetic, range coder, ANS family, FOR, PFOR-delta, S9, S16, varint, group varint, LSIC… • Как ещё поуменьшить данные? • roundoff, bitmaps, deltas, LUTs, dicts, LZ77/78, LZW, LZMA, PPM, PAQ, context modeling… • Какую подцепить мегабиблиотеку? • lz4, zstd, brotli, snappy, lzma… а то вдруг oodle
  • 53. D.I.Y. • Так эта! Как быстро/просто написать своё!? • Глянуть данные, посчитать частоты • Выдумать нехитрый (и лучше байтовый) код • Суперчастые случаи – предельно коротко • Редкие случаи – почти пофиг (!) насколько длинно • Частое паковать, редкое раздувать • На худой конец, возьмите varint / “mysql” / lsic • Иногда (иногда!) случается чудо • И быстрее, и компактнее (но таки дороже!!!)
  • 54. Вопросы? (Да, я всё ещё иногда читаю почту.) shodan@sphinxsearch.com