12 декабря 2006

Performance issues

Загрузил в свой проект реальные данные и ужаснулся - получил жуткие тормоза. Окно открывалось секунд 10. Нереально долго.

Вначале подумал что SQL запросы тормозят на большом количестве данных. Ан нет, летают. Собрал Release версию. Бегает намного быстрее, но все равно - окно открывает 2-3 секунды. Это конечно лучше чем 10 секунд у Debug версии, но мне 1) в бета-тестирование давать именно Debug версию, 2) самому отлаживат - Debug версию, 3) все равно очень долго.

Методом тыка (без профайлера) выяснил что больше всего тормозит lookup записей из таблиц. Причем в коде стоял простой std::find и комментарий "todo: использовать индексы для быстрого поиска". На тестовых данных все летало, а вот на реальных данных, когда есть 3000 записей к ним нужно lookup-ить 2000 записей получалось O(M*N) = 2000*3000 = 6000000. Реализовал индексирование - стало намного быстрее. Открытие окна - секунды 2. Причем и в debug и в release примерно одинаково.

Пробежал код глазами - обнаружил интересный момент. При загрузке данных с SQL сервера идет преобразование из COM-овского _variant_t в проприетарный CVariant. Строки загружаются очень весело: у CVariant есть метод SetString(const std::wstring&), а у _variant_t достать строку можно только сначала преобразовав его в _bstr_t (при этом строка копируется), затем в const wchar_t*. При передаче ее в SetString() сначала конструируется wstring (а это плюс еще одно копирование), затем идет непосредственно копирование строки внутри CVariant::SetString(). Вобщем куча копирований вместо одного. Придется вручную лезть внутрь _variant_t, благо он унаследован от tagVARIANT, который является структурой - то есть все внутренности - public. Но самое интересное - жуткие тормоза возникают совсем не из-за этого, а где-то в другом месте.

Это другое место оказалось повторной загрузкой одних и тех же данных. То есть если какому-то объекту понадобились какие-то данные, то он их просто грузит с сервера. И так несколько объектов вытягивают одну и ту же большую таблицу.

Естественно, необходимо реализовать кэш. Да так и планировалось - создать кэш. Но проблема создания такого кэша - его же надо обновлять. В дальнейшем все запросы (в том числе на изменение данных) должны проходить через сервер приложений, и он должен присылать по подписке сообщения типа "обновите кэш". Но сейчас временно (до первой беты) сервера приложений не планируется. Так как если его делать сразу, то хрен знает когда начнется бета-тестирование. А без сервера приложений некому прислать сообщение "обновите кэш" - используемый SQL Server этого не умеет.

Видимо придется пока временно делать таймер, который будет периодически наведываться на SQL сервер и узнавать, не пора ли обновить кэш.

PS. Как в таких случаях жить без профайлера? VTune $700 стоит. Может что попроще и побесплатней есть?

4 комментария:

kleptos комментирует...

А почему вы не используете для кеша проксирующий запросы обьект?

Raider комментирует...

Как раз все запросы идут через один объект. Он уже решает - взять из кэша или запросить с сервера.

Анонимный комментирует...

Может что попроще и побесплатней есть?
AMD CodeAnalyst?

Raider комментирует...

Если от просит процессор от AMD, то для меня это может стать большой проблемой - найти в офисе компьютер с таким процессором