Новый триал IBProvider-а [сборка 15302]. Ревизия кода
Привет всем.
Новый рабочий год начинается тяжко начал с ревизии и доработки существующего кода :). А конкретнее:
— Натравил на код анализаторы.
— Немного оптимизировал работу с низкоуровневыми конструкциями, на которых базируется вся инфраструктура.
Анализ кода.
1. Сначала я прогнал исходный код через компилятор (VS2012) с включенным 4-м уровнем предупреждений. Первоначальное число ошибок было в районе трех сотен. Сократил до 50-штук. Наиболее положительный эффект (в виде уменьшения размера бинарника) дало уничтожение недостижимого кода. Это были return-ы после выкидывания исключений.
Кроме того, были обнаружены и устранены потенциально опасные ситуации с неинициализированными переменными.
2. Потом я снова натравил на код статический анализатор кода 2012 студии. Изничтожил еще несколько предупреждений. В частности, анализатор выявил некритичную ошибку в использовании sprintf. Я вообще-то стараюсь не использовать эти муторные функции (то есть, практически совсем их не использую). И надо бы окончательно исключить их из кода.
3. Прогнал код через триал PVS Studio. Он нашел еще пару ошибок (к счастью, неопасных) созданных по китайской технологии copy&paste. И, в целом, также способствовал улучшению качества кода.
Моя первая реакция на результаты работы этого анализатора — «вот сукины дети!» 🙂
Оптимизация низкоуровневых примитивов.
В основе большинства конструкций провайдера лежат объекты со счетчиками ссылок. Управляются они смарт-указателями, которые автоматизируют инкременты (AddRef) и декременты (Release) этих счетчиков. Этот подход к конструированию применяется мною где-то с 2000 года, и время от времени появляются мысли по поводу оптимизации работы этих смарт-указателей.
В предпоследний раз оптимизация была связана с внедрением rvalue-reference, которая сократила число вызовов AddRef/Release при перемещении (move) смарт-указателей:
template<class T,class traits_data> inline t_smart_object_ptr<T,traits_data>::t_smart_object_ptr(self_type&& ptr) :m_ptr(__STL_MOVE_VALUE(ptr.m_ptr)) { ptr.m_ptr=NULL; }//t_smart_object_ptr - move constructor
А в этот раз я допер, что в коде вида:
t_smart_object_ptr<t_my_object> spObj(new t_my_object());
можно подсказать конструктору смарт-указателя, что передаваемый аргумент не равен NULL. Например так:
t_smart_object_ptr<t_my_object> spObj(not_null_ptr(new t_my_object()));
В результате будет вызываться не универсальный конструктор
template<class T,class traits_data> inline t_smart_object_ptr<T,traits_data>::t_smart_object_ptr(pointer const ptr) :m_ptr(ptr) { if(m_ptr) traits_data::increment_cntRef(m_ptr);//->add_ref(); }
а специализированный, в котором отсутствует «if»:
template<class T,class traits_data> template<class U> inline t_smart_object_ptr<T,traits_data>::t_smart_object_ptr(const t_not_null_ptr<U>& nn_ptr) :m_ptr(nn_ptr) { assert(m_ptr); traits_data::increment_cntRef(m_ptr);//->add_ref(); }
Кроме конструкторов, поддержка аналогичной подсказки была добавлена и для операторов присваивания.
После внедрения этой техники, несмотря на увеличение исходного кода, релизный бинарник уменьшился. И сохранил свою работоспособность. Что в очередной раз дало +100 к карме разработчиков компилятора студии.
Конечно, эта оптимизация даст 0% к производительности кода в реальных условиях. Я это точно знаю. Но все равно — приятно, что «пока еще могу» 🙂