Учимся Правильно Бенчмаркировать 2: Как Компилятор Наносит Удар В Спину

Получение достоверных контрольных показателей — это половина дела, а другая половина — правильная их интерпретация, изучение чего-то нового и умение их применять.

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

В результате я лучше усвоил, что происходит в отладке; Искал различия между студиями 2005 и 2008 годов (не нашел); Разобрался как ускорить отладочную сборку в 3 раза за пару минут (блокируемся против удара в спину); методом «бери и беги» я получил результаты, отличающиеся от авторских в 3,5 раза (адская мощь х64 в действии!); и ради интереса я сравнил плохой, бесполезный недовектор с хорошим (плохой оказался до 100 раз быстрее).

Подробности под катом.

Начну со ссылок на источники, а то потом тяжело искать.

Начальное сообщение , их тестовый код , мой тестовый код .

Устранив тряску, я вернулся к исходному вопросу: тормоза тормозят более чем в 100 раз, откуда они берутся? Чужой тест — это хорошо, но я написал свой и прогнал его более привычно.

Создавать проект было лениво, компилировать из командной строки гораздо быстрее.



cl2005 /O2 /EHsc 1.cpp std it++ res=49995000, 28.5 msec std ++it res=49995000, 28.6 msec my res=49995000, 19.0 msec cl2005 /EHsc 1.cpp std it++ res=49995000, 534.2 msec std ++it res=49995000, 437.6 msec my res=49995000, 69.9 msec

Упс.

Результаты, правда, разные: у тех ребят в 30 раз медленнее, у меня — всего в 10. У тех ребят постинкремент замедляется в 3 раза, у меня — около 20%.

Что я делаю не так? Неужели различия между компилятором и запрещенной библиотекой настолько серьезны? Что ж, давайте установим 2008 и проверим.



cl2008 /O2 /EHsc 1.cpp std it++ res=49995000, 64.3 msec std ++it res=49995000, 63.4 msec my res=49995000, 19.0 msec cl2008 /EHsc 1.cpp std it++ res=49995000, 732.1 msec std ++it res=49995000, 678.8 msec my res=49995000, 70.0 msec

Хм.

Отличия действительно есть, только в обратную сторону: релизная сборка VS 2005, правда, была в три раза быстрее.

Есть улучшения; Судя по всему, SCL в студии номер 2008 стал раза в три безопаснее, может быть.

с _SECURE_SCL 0 скорость такая же.

(Забегая вперед, большинство других тестов тоже практически такие же.

) Что еще я делаю не так? Компилятор теперь тот же, std::vector вроде тот же, вывод понятен: компилирую неправильно.

Я имею в виду, что ключи компилятора расходятся.

Смотрим решение, там довольно разросшаяся командная строка.



/Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_UNICODE" /D "UNICODE" /Gm /EHsc /RTC1 /MDd /Fo"Debug\\" /Fd"Debug\vc90.pdb" /W3 /nologo /c /ZI /TP /errorReport:prompt

Благодаря революционному чутью и грубой силе можно быстро выбросить неважные ключи и оставить важные.

Есть три важных ключа: а) /MDd, б) /RTC1, в) /ZI. Результаты выглядят следующим образом.



cl2008 /Od /EHsc /MDd 1.cpp std it++ res=49995000, 6026.1 msec std ++it res=49995000, 1953.9 msec my res=49995000, 66.5 msec cl2008 /Od /EHsc /MDd /RTC1 1.cpp std it++ res=49995000, 7572.0 msec std ++it res=49995000, 2385.3 msec my res=49995000, 101.8 msec cl2008 /Od /EHsc /MDd /RTC1 /ZI 1.cpp std it++ res=49995000, 18722.0 msec std ++it res=49995000, 7131.8 msec my res=49995000, 511.9 msec

Ну а теперь все как у людей: обход вектора тормозит как гад, а постинкремент начал тормозить.

Хороший! Вы можете понять, что происходит. За пару минут чтения кл/? Раскрываются три простые, понятные вещи (по одной на ключ), которые пока еще новые (см.

хорошо забытые старые) и потому удивительные.

/MDd связал библиотеку отладки (что не важно) и автоматически включил /D _DEBUG (что важно).

Из-за этого в SCL был включен дополнительный пакет проверок в дополнение к включенным _SECURE_SCL, который успешно замедлил пост-инкремент в 8 раз, а пре-инкремент - в жалкие 3 раза.

/RTC1 включает проверки на подделку стека и неинициализированные переменные.

Если пройти дизассем в отладчике, то можно увидеть, что во внутреннем цикле происходит пять вызовов разных функций (++, !=, * и два деструктора).

Каждый вызов надо проверять: а вдруг он примет и как разобьет стек! Для этого в стек помещается чуть менее 256 байт маркерного материала для каждого (каждого) вызова того или иного оператора, перегруженного в STL. Пока-пока, еще 20% уже не самой лучшей производительности.

/ZI, однако, превосходит всех и забирает банк домой.

Это «Изменить и продолжить».

Исправление и перезапуск программы на лету наверное удобно (сам не знаю, не пользуюсь).

За удобство надо платить и цена еще в 3 раза превышает тормоза.

В общей сложности в дебаге постинкремент тормозит эпик в 291 раз (!!!) по сравнению с релизом.

Сразу возникает вопрос: почему у меня целых 291 раз, а у этих ребят только 95?! Собираю исходный тест, терпения ждать конца не хватает, уменьшаю счет в 10 раз, время в голове умножаю на 10. Получается, что

it++, x86, release: 1.00 ++it, x86, release: 1.00 it++, x86, debug: 275.7 ++it, x86, debug: 101.9 it++, x64, release: 0.87779 ++it, x64, release: 0.87753 it++, x64, debug: 83.2849 ++it, x64, debug: 27.1557

Судя по цифрам, в релизе с моего х86 взяли подоходный налог в размере 13%, а в дебаге меня полностью ограбили, оставив только трусы, носки и тапочки.

Исчезающе обидно, но это нормально.

Я все равно от ХР не откажусь, семерка для меня сложна, слишком аэропрозрачна, плюс ждем второй сервиспак.

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

Он потребляет _SECURE_SCL чуть больше 2 раз, отключая оптимизацию 5 раз, от 3 раз (++it) до 8 раз (it++) потребляет проверки под _DEBUG, 1,2 раза выбрасывает /RTC, а с финальным аккордом выбрасывает /ZI еще до 3 раз, итого 2*5*8*1,2*3=288, курить по крупинке, весь код в.

проверки, замедления 300 (триста) раз.

Конечно, вы едите курицу руками.

Из вечных трех теперь остался только один вопрос: что делать? В общем, можно многое сделать, но это займет много времени.

Однако за 2 минуты можно сделать 2 полезных дела.

Bo1x, если принять волевое решение не использовать Edit and Continue (то есть, если это вообще работает на вашем проекте) и генерировать обычные советские PDB, то он начинает работать в 3 раза быстрее, если не во все 5 раз.

Чеки Bo2x, /RTC — это, конечно, не круто.

Однако! Чтение MSDN открывает интересную прагму runtime_checks. Те.

Вы можете отключить эти проверки индивидуально для особо частых функций в проекте или для всего STL. Окружаем внешний раздел include двумя линиями и наслаждаемся.



#pragma runtime_checks("",off) .

#pragma runtime_checks("",restore)

Мы возвращаем свои честные 6 секунд вместо 18 секунд при использовании it++, 2 секунды вместо 7 секунд при использовании ++it. Вывод о необходимости использования ++ убедительно подтверждается.

Отладочная сборка ускорена примерно в 3 раза.

Выгода! Пробуем применить полученные знания к исходному тесту, получается аналогично.

По крайней мере на старом добром x86.

vanilla it++, x86, debug: 275.7 ++it, x86, debug: 101.9 #pragma runtime_checks, /Zi instead of /ZI it++, x86, debug: 102.2 ++it, x86, debug: 30.0

Плохой, кривой, никчемный и примитивный недовектор, который вообще ничего не умеет, показывает в дебаге свои честные 0,066 секунды, т.е.

В 90 раз быстрее, чем it++, или в 30 раз быстрее, чем ++it. Раз 100 в начале поста я соврал, округлили.

У него всего одна проверка: корректность индекса при доступе (которая, однако, покрывает примерно 99% необходимых проверок от вектора); фокус с пре- и пост-инкрементами отсутствует вместе с итераторами.

Это ничего не значит. Автор ни на что не намекает. Бенчмарк явно синтетический.

Вектор был написан буквально за 3 минуты и функционал соответствующий.

не существует по сравнению с STL. Скорость отладочной сборки не обязательно важна; может потребоваться дополнительная проверка; скорость развития всегда важна для всех; Профайлер всегда скажет вам правду.

Помните про _SECURE_SCL, про _DEBUG, про ключи компилятора, про #pragma runtime_checks. Эффект, ну, может быть ошеломляющим.

(Лично меня убила разница в 300 раз.

А разница в 3-5 раз из-за /ЗИ вместо /Зи после этого тоже съела труп.

«Я знал об этом, но не догадался.

») Правильные ориентиры для вас.

И быстрая отладка.

Теги: #C++ #бенчмаркинг #отладка #C++

Вместе с данным постом часто просматривают:

Автор Статьи


Зарегистрирован: 2019-12-10 15:07:06
Баллов опыта: 0
Всего постов на сайте: 0
Всего комментарий на сайте: 0
Dima Manisha

Dima Manisha

Эксперт Wmlog. Профессиональный веб-мастер, SEO-специалист, дизайнер, маркетолог и интернет-предприниматель.