Андрей Карпов
ООО "Системы программной верификации"АннотацияВ этой статье Анатолий Кузнецов отвечает на вопросы и рассказывает об открытой библиотеке BitMagic C++ Library.
ВведениеРегулярно просматривая ресурсы интернета связанные с тематикой 64-битного программирования, я неоднократно встречал упоминание библиотеки BitMagic и то, что она получила существенные преимущества от использования 64-битности. Я решил связаться с автором библиотеки и предложить ему рассказать в интервью о своих исследованиях и разработках.
Вопросы задает: Андрей Карпов - сотрудник компании "Системы программной верификации", занимающийся созданием инструмента
PVS-Studio для проверки современных приложений на языке Си++.
На вопросы отвечает: Анатолий Кузнецов - старший инженер по разработке программного обеспечения в NCBI. Является разработчиком открытой библиотеки
BitMagic C++ Library.
Здравствуйте, Анатолий. Расскажите, пожалуйста, немного о себе. Какими проектами Вы занимаетесь?Здравствуйте Андрей!
Я старший инженер по разработке программного обеспечения, в данный момент я работаю в группе поиска и визуализации биомолекулярной информации
NCBI (National Center for Biotechnology Information). Помимо своей основной деятельности я являюсь основным разработчиком и архитектором открытой библиотеки BitMagic C++ Library.
По образованию я инженер-экономист, выпускник Нижегородского Университета им. Лобачевского.
Что такое библиотека BitMagic?Библиотека BitMagic разрабатывалась как универсальная библиотека шаблонов для работы с компрессированными битовыми векторами. Библиотека решает несколько задач:
- Предоставляет битовый контейнер, по-настоящему совместимый по идеологии с STL. Это значит, что контейнер должен поддерживать итераторы, аллокаторы памяти, взаимодействовать с алгоритмами и другими STL контейнерами.
- Библиотека умеет эффективно оперировать с очень длинными и разреженными векторами.
- Предоставляется возможность сериализации векторов для последующей записи их в базы данных или пересылке по сети.
- Разработчику предоставляется набор алгоритмов для реализации теоретико-множественных операций и подсчета расстояний и метрик подобия в многомерных двоичных пространствах.
- Значительное внимание уделяется оптимизации под распространенные системы ускорения расчетов, такие как SSE.
При решении каких задач BitMagic представляет наибольший интерес для разработчиков?Библиотека получилась довольно универсальная, и перечислить все возможные применения, наверное, будет не просто. В данный момент библиотека наиболее интересна в следующих областях:
- Построение битовых и инвертированных индексов для полнотекстовых поисковых систем, ускорение операций реляционной алгебры (AND, OR, JOIN, и так далее).
- Разработка нестандартных расширений и индексов для существующих СУБД (Oracle Cartridges, MS SQL extended stored procedures). Такие расширения, как правило, помогают интегрировать в СУБД научные, географические или какие-то другие нестандартные данные.
- Разработка алгоритмов анализа данных (data mining).
- Разработка бездисковых индексов и баз данных (in-memory databases).
- Разработка систем точного разграничения доступа с большим количеством объектов (базы данных повышенной секретности с разграничением доступа к отдельным полям и колонкам).
- Системы управления задачами (на вычислительном кластере), системы real-time отслеживание состояния задач, хранение состояний задач описываемых как конечные автоматы (Finite State Machines).
- Задачи представления и хранения связанных графов
Какова история создания библиотеки BitMagic? Что послужило поводом к ее созданию?Я и мои коллеги продолжительное время занимались задачами, связанными с большими базами данных, системами анализа и визуализации. Самую первую рабочую версию, демонстрирующую возможности битовых векторов показал Максим Шеманарев (он является разработчиком великолепной библиотеки 2D векторной графики Antigrain Geometry:
http://www.antigrain.com). Потом некоторые идеи по эквивалентному представлению множеств были описаны инженером из Европы Koen Van Damm, работавшим над парсерами языков программирования для верификации сложных систем. Были и другие источники. Все это я решил как-то систематизировать и представить в виде библиотеки пригодной для многократного повторного использования в разных проектах.
На каких условиях распространяется библиотека BitMagic? Где можно ее скачать?Библиотека свободна для коммерческого и некоммерческого использования, доступна в виде исходных текстов. Единственным ограничением является требование упоминания библиотеки и авторов при использовании в конечном продукте.
С материалами можно ознакомиться тут:
http://bmagic.sourceforge.net.
Правильно ли я понимаю, что библиотека BitMagic получает существенные преимущества при компиляции в 64-битном варианте?Действительно, библиотека использует серию оптимизационных приемов ускоряющих работу в 64-битных системах или системах с SIMD командами (128-bit
SSE2).
Факторы, ускоряющие выполнение алгоритмов:
- широкое машинное слово (логические операции исполняются над широким словом);
- программисту (и компилятору) доступны дополнительные регистры, не так критичен дефицит регистров (есть такой наследственный недостаток у архитектуры x86);
- выравнивание памяти часто ускоряет работу (128-битное выравнивание адресов дает неплохой результат);
- ну и конечно возможность помещать в память одной программы больше объектов и обрабатываемых данных. Это всем понятный и бесценный плюс 64-битного варианта.
В данный момент наиболее быстрым вариантом является использование 128-bit SSE2 оптимизации в 64-битной программе. Такой режим совмещает удвоенное количество
x86 регистров и широкое машинное слово для выполнения логических операций.
64-битные системы и программы переживают сейчас настоящий ренессанс. Перевод программ под 64 бита будет происходить быстрее, чем в переход с 16 на 32. Этому будет способствовать выход на массовый рынок 64-битных версий Windows и доступный инструментарий (такой как разрабатываете ваша компания). В условиях постоянного роста сложности систем и объема используемого в них кода такой инструментарий, как
PVS-Studio, является хорошим подспорьем, так как сокращает трудозатраты и увеличивает скорость вывода продуктов на рынок.
Расскажите о методах компрессии, используемых в BitMagic.Текущая версия 3.6.0 библиотеки использует несколько методов сжатия.
- "Битвектора" в памяти разбиты на блоки. Если блок не занят или занят полностью - он не аллoцируется. То есть программист свободен устанавливать биты в очень далеком от нуля диапазоне. Установка бита 100,000,000 не вызывает взрыв в потреблении памяти, часто свойственный векторам с плоской линейной моделью.
- Блоки в памяти могут иметь эквивалентное представление в виде площадок - гапов. Фактически это вариант RLE кодирования. В отличие от RLE, наша библиотека не теряет возможности выполнять логические операции или адресовать отдельные биты (random bit access).
- При сериализации "битвекторов" применяется набор других методов: преобразование в списки целых чисел (отражающих нули или единицы) и кодирование списков методом Elias Gamma Coding. При использовании данных методов теряется возможность адресовать отдельные биты, но для записи на диск это не так критично, по сравнению с сокращением затрат на хранение и ввод-вывод.
Не могли бы Вы привести пару примеров кода, демонстрирующих использование библиотеки BitMagic?Один из примеров просто создает 2 вектора, проводит их инициализацию и выполняет логическую операцию AND. Далее используется класс enumerator для итерирования и печати значений, сохраненных в векторе.
Код |
#include <iostream> #include "bm.h" using namespace std; int main(void) { bm::bvector<> bv; bv[10] = true; bv[100] = true; bv[10000] = true; bm::bvector<> bv2(bv); bv2[10000] = false; bv &= bv2; bm::bvector<>::enumerator en = bv.first(); bm::bvector<>::enumerator en_end = bv.end(); for (; en < en_end; ++en) { cout << *en << endl; } return 0; } |
Следующий пример демонстрирует сериализацию векторов и использование режима компрессии.
Код |
#include <stdlib.h> #include <iostream> #include "bm.h" #include "bmserial.h" using namespace std; // This procedure creates very dense bitvector. // The resulting set will consists mostly from ON (1) bits // interrupted with small gaps of 0 bits. // void fill_bvector(bm::bvector<>* bv) { for (unsigned i = 0; i < MAX_VALUE; ++i) { if (rand() % 2500) { bv->set_bit(i); } } } void print_statistics(const bm::bvector<>& bv) { bm::bvector<>::statistics st; bv.calc_stat(&st); cout << "Bits count:" << bv.count() << endl; cout << "Bit blocks:" << st.bit_blocks << endl; cout << "GAP blocks:" << st.gap_blocks << endl; cout << "Memory used:"<< st.memory_used << endl; cout << "Max.serialize mem.:" << st.max_serialize_mem << endl << endl;; } unsigned char* serialize_bvector( bm::serializer<bm::bvector<> >& bvs, bm::bvector<>& bv) { // It is reccomended to optimize // vector before serialization. bv.optimize(); bm::bvector<>::statistics st; bv.calc_stat(&st); cout << "Bits count:" << bv.count() << endl; cout << "Bit blocks:" << st.bit_blocks << endl; cout << "GAP blocks:" << st.gap_blocks << endl; cout << "Memory used:"<< st.memory_used << endl; cout << "Max.serialize mem.:" << st.max_serialize_mem << endl; // Allocate serialization buffer. unsigned char* buf = new unsigned char[st.max_serialize_mem]; // Serialization to memory. unsigned len = bvs.serialize(bv, buf, 0); cout << "Serialized size:" << len << endl << endl; return buf; } int main(void) { bm::bvector<> bv1; bm::bvector<> bv2; // set DGAP compression mode ON bv2.set_new_blocks_strat(bm::BM_GAP); fill_bvector(&bv1); fill_bvector(&bv2); // Prepare a serializer class // for best performance it is best // to create serilizer once and reuse it // (saves a lot of memory allocations) // bm::serializer<bm::bvector<> > bvs; // next settings provide lowest serilized size bvs.byte_order_serialization(false); bvs.gap_length_serialization(false); bvs.set_compression_level(4); unsigned char* buf1 = serialize_bvector(bvs, bv1); unsigned char* buf2 = serialize_bvector(bvs, bv2); // Serialized bvectors (buf1 and buf2) now ready to be // saved to a database, file or send over a network. // ... // Deserialization. bm::bvector<> bv3; // As a result of desrialization bv3 // will contain all bits from // bv1 and bv3: // bv3 = bv1 OR bv2 bm::deserialize(bv3, buf1); bm::deserialize(bv3, buf2); print_statistics(bv3); // After a complex operation // we can try to optimize bv3. bv3.optimize(); print_statistics(bv3); delete [] buf1; delete [] buf2; return 0; } |
Какие планы по развитию библиотеки BitMagic? Очень хочется реализовать несколько новых методов компрессии векторов с возможностью параллельной обработки данных.
В связи с массовым выходом Intel Core i5-i7-i9 целесообразно выпустить версию библиотеки для SSE 4.2. Компания Intel добавила несколько интересных возможностей, которые можно эффективно использовать. Самым интересным является аппаратная поддержка расчета числа битов (Population Count).
Ведутся эксперименты с nVidia CUDA и другими GPGPU. Графические карты сегодня предоставляют возможность выполнения целочисленных и логических операций - есть возможность использовать их ресурсы для алгоритмов работы с множествами и компрессии.
Библиографический список- Elias Gamma encoding of bit-vector Delta gaps (D-Gaps). http://bmagic.sourceforge.net/dGap-gamma.html
- Hierarchical Compression. http://bmagic.sourceforge.net/hCompression.html
- D-Gap Compression. http://bmagic.sourceforge.net/dGap.html
- 64-bit Programming And Optimization. http://bmagic.sourceforge.net/bm64opt.html
- Optimization of memory allocations. http://bmagic.sourceforge.net/memalloc.html
- Bitvector as a container. http://bmagic.sourceforge.net/enum.html
- 128-bit SSE2 optimization. http://bmagic.sourceforge.net/bmsse2opt.html
- Using BM library in memory saving mode. http://bmagic.sourceforge.net/memsave.html
- Efficient distance metrics. http://bmagic.sourceforge.net/distopt.html