(495) 925-0049, ITShop интернет-магазин 229-0436, Учебный Центр 925-0049
  Главная страница Карта сайта Контакты
Поиск
Вход
Регистрация
Рассылки сайта
 
 
 
 
 

О тонкостях повышения performance на С++, или как делать не надо

Источник: habrahabr
viklequick

image
Однажды, много лет назад, пришел ко мне клиент, и слезно умолял поручил разобраться в одном чудесном проекте, и повысить скорость работы.

Вкратце, задача была такой - есть некий робот на С++, обдирающий HTML страницы, и собранное складывающий в БД (MySQL). С массой функционала и вебом на LAMP - но это к повествованию отношения не имеет.

Предыдущая команда умудрилась на 4-ядерном Xeon в облаке получить фантастическую скорость сбора аж в 2 страницы в секунду, при 100% утилизации CPU как сборщика, так и БД на отдельном таком же сервере. 

К слову, поняв что они не справляются - "команда крепких профессионалов" из г. Бангалор сдалась и разбежалась, так что кроме горки исходников - "ничего! помимо бус" (С).

О тонкостях наведения порядка в PHP и в схеме БД поговорим как-нибудь в другой раз, приведу только один пример приехавшего к нам мастерства.

Приступаем к вскрытию

image
Столь серьезная загрузка БД меня заинтересовала в первую очередь. Включаю детальное логирование - и начинаю вырывать на себе волосы во всех местах вот оно. 

Задачи из интерфейса разумеется складывались в БД, а робот 50 раз в секунду опрашивал - а не появилась ли новая задача? Причем данные естественно разложены так, как удобно интерфейсу, а не роботу. Итог - три inner join в запросе. 

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

select * from new_table where status = Pending

Новая картинка - сборщик по-прежнему занят на 100%, БД на 2%, теперь четыре страницы в секунду.

Берем в руки профилировщик

image
И внезапно выясняется, что 80% времени выполнения занимают чудные методыEnterCriticalSection и LeaveCriticalSection. А вызываются они (предсказуемо) из стандартного аллокатора одной известной компании.

Вечер перестает быть томным, а я понимаю что работы - много и переписывать придется от души.

И разумеется - парсить HTML быдлокодеры мои предшественники предпочитали пачкой регулярных выражений, ведь SAX - это так сложно.

Самое время ознакомиться - а что было улучшено до меня?

 

 

 

Об опасности premature optimizations мысленным лучом

Видя, что БД загружена на 100%, ребята были твердо уверены, что тормозит вставка в список новых URL для обработки. 

Я даже затрудняюсь понять - чем они руководствовались, оптимизируя именно этот кусок кода. Но сам подход! У нас по идее тормозит вот тут, давайте мы затормозим еще.

Для этого, они придумали такие трюки:

  1. Очередь асинхронных запросов на insert
  2. Огромная HashMap в памяти, самописная, с giant lock, которая запоминала все пройденные URL в памяти. А так как это был сервер - то его после таких оптимизаций приходилось регулярно перезапускать. Очистку своего кэша они не доделали.
  3. Масса магических констант, например - для обработки следующей партии URL из БД беретсмя не более 400 записей. Почему 400? А подобрали.
  4. Количество "писателей" в БД было велико, и каждый пытался свою часть впихнуть в цикле, вдруг повезет.

И конечно же много других перлов было в наличии.

Вообще, наблюдать за эволюциями кода было весьма поучительно. Благо в запасливости не откажешь - все аккуратно закомментировано. Вот примерно так

void GodClass::PlaceToDB(const Foo* bar, ...) {
/* тут код с вариантом номер 1, закомментарен */
/* тут код с вариантом номер 2 - копипаст первого и немного изменений, закомментарен  */
/* тут код с вариантом номер 3 - еще изменили, не забыв скопировать вариант номер два, закомментарен  */
....
/* тут вариант номер N-1, уже ничего общего не имеет с первым вариантом, закомментарен  */
// а тут наконец-то вариант рабочий
}

Что делал я

image
Разумеется, все трюки были немедленно выброшены, я вернул синхронную вставку, а в БД был повешен constraint, чтобы отсекал дубли (вместо плясок с giant lock и самописным hashmap). 

Автоинкрементные поля также убрал, вместо них вставил UUID (для подсчета нового значения может приползать неявный lock table). Заодно серьезно уменьшил таблицу, а то по 20К на строчку - неудивительно что БД проседает.

Магические константы также убрал, вместо них сделал нормальный thread pool с общей очередью задач и отдельной ниткой заполнения очереди, чтобы не пустовала и не переполнялась.

Результат - 15 страниц в секунду.

Однако, повторное профилирование не показало прорывных улучшений. Конечно, ускорение в 7 раз за счет улучшения архитектуры - это тоже хлеб, но - мало. Ведь по сути все исходные косяки остались, я убрал только вусмерть заоптимизированные куски.

Регулярные выражения для разбора мегабайтных структурированных файлов - это плохо

image
Продолжаю изучать то, что сделано до меня, наслаждаюсь подходом неизвестных мне авторов. 

Ме-то-ди-ка!  

С грациозностью трактора ребята решали проблему доставания данных так (каждому действу свой набор регулярных выражений).

  • Вырезали все комментарии в HTML
  • Вырезали комментарии в JavaScript
  • Вырезали теги script
  • Вырезали теги style
  • Вынули две цифры из head
  • Вырезали все кроме body
  • Теперь собрали все "a href" и вырезали их
  • В body вырезали все ненужные div и table, а также картинки
  • После чего убрали табличную разметку
  • В оставшемся убирали теги p, strong, em, i, b, г и т. д.
  • И наконец в оставшемся plain text достали еще три цифры

Удивительно с таким подходом, что оно хотя бы 2 страницы в секунду пережевывало.

Понятно, сами выражения после их тюнинга я не привожу - это огромная простыня нечитаемых закорючек.

Это еще не все - разумеется, была использована правильная библиотека boost, а все операции проводились над std::string ( правильно - а куда еще HTML складывать? char* не концептуально! Только хардкор! ). Вот отсюда и безумное количество реаллокаций памяти.

Беру char* и простенький парсер HTML в SAX-style, нужные цифры запоминаю, параллельно вытаскиваю URL. Два дня работы, и вот.

Результат - 200 страниц в секунду.

Уже приемлемо, но - мало. Всего в 100 раз.

Еще один подход к снаряду

image
Перехожу к результатам нового профилирования. Стало лучше, но аллокаций все еще много, и на первое место вылез почему-то бустовский to_lower().

Первое, что бросается в глаза - это могучий класс URL, цельнотянутый из Java.  Ну правильно - ведь это С++, он по любому быстрее будет, подумаешь что аллокаторы разные . Так что пачка копий и substring() - наше индусское все. И конечно же to_lower прямо к URL::host применять ни-ни - надо на каждом сравнении и упоминании и непременно boost-ом.

Убираю чрезмерное употребление to_lower(), переписываю URL на char* без переаллокаций вместо std::string. Заодно оптимизирую пару циклов.

Результат - 300 страниц в секунду.

На этом закончил, ускорение было достигнуто в 150 раз, хотя еще были резервы для ускорения. И так убил больше 2х недель.

Выводы

image
Выводы как всегда - классика жанра. Используйте инструменты при оценке производительности, не выдумывайте из головы. Ширше (или ширее) пользуйтесь готовыми библиотеками, вместо закатывания солнца вручную.

И да пребудет с вами святой Коннектий высокий перформанс!

Ссылки по теме


 Распечатать »
 Правила публикации »
  Написать редактору 
 Рекомендовать » Дата публикации: 13.11.2012 
 

Магазин программного обеспечения   WWW.ITSHOP.RU
Microsoft Office 365 для Дома 32-bit/x64. 5 ПК/Mac + 5 Планшетов + 5 Телефонов. Подписка на 1 год.
Microsoft Office 365 Персональный 32-bit/x64. 1 ПК/MAC + 1 Планшет + 1 Телефон. Все языки. Подписка на 1 год.
Microsoft 365 Business Standard (corporate)
Microsoft 365 Apps for business (corporate)
Microsoft Windows Professional 10, Электронный ключ
 
Другие предложения...
 
Курсы обучения   WWW.ITSHOP.RU
 
Другие предложения...
 
Магазин сертификационных экзаменов   WWW.ITSHOP.RU
 
Другие предложения...
 
3D Принтеры | 3D Печать   WWW.ITSHOP.RU
 
Другие предложения...
 
Новости по теме
 
Рассылки Subscribe.ru
Информационные технологии: CASE, RAD, ERP, OLAP
Безопасность компьютерных сетей и защита информации
Новости ITShop.ru - ПО, книги, документация, курсы обучения
Программирование на Microsoft Access
CASE-технологии
eManual - электронные книги и техническая документация
Программирование на Visual С++
 
Статьи по теме
 
Новинки каталога Download
 
Исходники
 
Документация
 
 



    
rambler's top100 Rambler's Top100