вторник, 31 мая 2011 г.

fastdb query

Существует такая библиотека, fastdb. Это объектная in-memory база данных. Мы используем ее в своем проекте, чтобы хранить конфигурацию сервера. Администратор вносит изменения в конфигурацию, при этом программа управления - весьма тонкий клиент, не обладающей собственной логикой. Вся логика находится на сервере, который должен проверить, что то, что сделал администратор не противоречит функционированию системы в целом.

Для проверки в основном использовались dbQuery. Мне всегда казалось это неудобным, поскольку в Query не так то просто подсунуть программные константы. Кроме того мучили сомнения относительно эффективности данного подхода, которые и сподвигли меня на измерение производительности.

Запросов достаточно много, на каждый случай - свой. Причем каждый запрос характеризует невозможную ситуацию, если какой нибудь объект удовлетворяет критериям запроса - это фейл. Основной вопрос - насколько dbQuery быстрее циклического перебора полной выборки. Особенно учитывая, что в одном цикле можно проверить несколько кейсов. Но давайте по порядку.

Надо сказать что я не сильно заморачивался и тестирование получилось достаточно поверхностное.

Для измерения была создана тестовая база с миллионом достататочно простых структурок.
struct dbItem {
db_nat4 id;
db_nat4 type;
const char *name;
TYPE_DESCRIPTOR((KEY(id, AUTOINCREMENT|INDEXED), FIELD(type), FIELD(name)));
};
Строковой параметр ввел специально, чтобы поднапрячь движок. Все поля, кроме id, который AUTOINCREMENT, заполнил случайным образом. Общий размер базы данных составил гигабайт. Нетипично большая база, но думаю, она более показательна для наших измерений.

Вообще dbQuery достаточно тяжелый объект, поскольку он должен интерпретировать запрос, который в него передается. Внутри он хранит оптимизированное представление. Если запрос сам по себе достаточно неизменен, имеет смысл использовать ключевое слово static. Но на выборке в миллион элементов эффект от статичности dbQuery совершенно нивелируется.

На моей выборке один select выполняется примерно 130мс. Но только в том случае, если в запросе используется неиндексированное поле. Если же поле используется индексированное, то время выборки сокращается до 500нс! Такая же скорость наблюдается при использовании selectByKey. Для сравнения я провел аналогичные замеры для map - он справился с поиском за 100нс.

Индексированные поля показывают выдающиеся результаты, но к сожалению наши выборки строятся на разных полях. Все же поля не станешь делать индексированными! И не стоит забывать о цели нашего тестирования. Нам ведь хотелось сравнить запросы с простым перебором.

А простой перебор без запросов справился с этим делом за 80мс. Что на ~30% быстрее, чем аналогичный query. При этом, если мы будем в простой перебор добавлять дополнительные условия, это не будет нам стоить практически ничего. В то время как каждый новый dbQuery стоит 130мс...

Для сравнения - поиск по такой же выборке, загруженной в list, занимает 9мс. Загруженной в vector - 2мс...

Резюмируя можно сказать, что dbQuery позволят вам получить значительный выигрыш, если вы грамотно используете в запросах индексированные поля. В противном случае простой перебор окажется быстрее. Но с другой стороны dbQuery кому-то могут показаться более наглядными, если скорость не критична.

Кроме того интересовал вопрос - стоит ли кешировать значения из БД в памяти? Если доступ к элементам БД осуществляется через индексированное поле - кешировать однозначно не нужно, по моему это только усложняет код, и все из за жалких 400 наносекунд. :)

Теперь можно убрать нафиг эти dbQuery, и мы получим возможность абстрагироваться от БД в плане проверок.