+7 (495) 229-0436   shopadmin@itshop.ru 119334, г. Москва, ул. Бардина, д. 4, корп. 3
 
 
Вход
 
 
Каталог
 
 
Подписка на новости
Новости ITShop
Windows 7 и Office: Новости и советы
Обучение и сертификация Microsoft
Вопросы и ответы по MSSQLServer
Delphi - проблемы и решения
Adobe Photoshop: алхимия дизайна
 
Ваш отзыв
Оцените качество магазина ITShop.ru на Яндекс.Маркете. Если вам нравится наш магазин - скажите об этом Google!
 
 
Способы оплаты
 
Курс расчета
 
 1 у.е. = 91.78 руб.
 
 Цены показывать:
 
 
 
 
  
Новости, статьи, акции
 

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

13.11.2012 15:40
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
Выводы как всегда - классика жанра. Используйте инструменты при оценке производительности, не выдумывайте из головы. Ширше (или ширее) пользуйтесь готовыми библиотеками, вместо закатывания солнца вручную.

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

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

  
Помощь
Задать вопрос
 программы
 обучение
 экзамены
 компьютеры
Бесплатный звонок
ICQ-консультанты
Skype-консультанты

Общая справка
Как оформить заказ
Тарифы доставки
Способы оплаты
Прайс-лист
Карта сайта
 
Бестселлеры
Курсы обучения "Atlassian JIRA - система управления проектами и задачами на предприятии"
Microsoft Windows 10 Профессиональная 32-bit/64-bit. Все языки. Электронный ключ
Microsoft Office для Дома и Учебы 2019. Все языки. Электронный ключ
Курс "Oracle. Программирование на SQL и PL/SQL"
Курс "Основы TOGAF® 9"
Microsoft Office 365 Персональный 32-bit/x64. 1 ПК/MAC + 1 Планшет + 1 Телефон. Все языки. Подписка на 1 год. Электронный ключ
Курс "Нотация BPMN 2.0. Ее использование для моделирования бизнес-процессов и их регламентации"
 

О нас
Интернет-магазин ITShop.ru предлагает широкий спектр услуг информационных технологий и ПО.

На протяжении многих лет интернет-магазин предлагает товары и услуги, ориентированные на бизнес-пользователей и специалистов по информационным технологиям.

Хорошие отзывы постоянных клиентов и высокий уровень специалистов позволяет получить наивысший результат при совместной работе.

В нашем магазине вы можете приобрести лицензионное ПО выбрав необходимое из широкого спектра и ассортимента по самым доступным ценам. Наши менеджеры любезно помогут определиться с выбором ПО, которое необходимо именно вам. Также мы проводим учебные курсы. Мы приглашаем к сотрудничеству учебные центры, организаторов семинаров и бизнес-тренингов, преподавателей. Сфера сотрудничества - продвижение бизнес-тренингов и курсов обучения по информационным технологиям.



 

О нас

 
Главная
Каталог
Новинки
Акции
Вакансии
 

Помощь

 
Общая справка
Как оформить заказ
Тарифы доставки
Способы оплаты
Прайс-лист
Карта сайта
 

Способы оплаты

 

Проекты Interface Ltd.

 
Interface.ru   ITShop.ru   Interface.ru/training   Olap.ru   ITnews.ru  
 

119334, г. Москва, ул. Бардина, д. 4, корп. 3
+7 (495) 229-0436   shopadmin@itshop.ru
Проверить аттестат
© ООО "Interface Ltd."
Продаем программное обеспечение с 1990 года