Урок №129. Измерение времени выполнения (тайминг) кода

  Юрий  | 

  |

  Обновл. 24 Сен 2021  | 

 57199

 ǀ   12 

Иногда, в процессе написания кода, вы можете столкнуться с ситуациями, когда не будете уверены, какая из двух функций окажется более эффективной (предполагается, что конечный результат у обеих функций одинаковый). Как это определить?

Один из самых простых способов — засечь время выполнения каждого из фрагментов кода. В C++11 это делается через библиотеку chrono. Мы можем легко инкапсулировать весь необходимый нам функционал в класс, который затем будем использовать в наших собственных программах.

Вот класс:

Для его использования нужно определить объект класса Timer в верхней части функции main() (или откуда вы хотите начинать отсчет), а затем просто вызвать метод elapsed() после части кода, которую вы проверяете:

Рассмотрим реальный пример, где нужно отсортировать массив из 10 000 элементов. Воспользуемся алгоритмом сортировки методом выбора:

На компьютере автора результат трех прогонов кода составляет 0.0508, 0.0507 и 0.0499 секунды, т.е. около 0.05 секунды.

Теперь проделаем то же самое, но с std::sort из Стандартной библиотеки C++:

Результаты трех прогонов на компьютере автора составляют 0.000694, 0.000693 и 0.000697 секунды, т.e. около 0.0007 секунды.

Таким образом, алгоритм std::sort() в 75 раз быстрее, чем сортировка, которую написали мы сами!

Что влияет на тайминг кода?

Тайминг кода является достаточно простым и прозрачным, но ваши результаты могут существенно отличаться из-за ряда вещей:

Во-первых, убедитесь, что вы используете режим конфигурации «Release», а не «Debug». Во время режима «Debug» оптимизация обычно отключена, а она может оказывать значительное влияние на результаты. Например, в конфигурации «Debug», выполнение сортировки элементов массива через std::sort() на компьютере автора заняло 0.0237 секунды, что в 34 раза больше, нежели в конфигурации «Release»!

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

Выполняйте тайминг как минимум 3 раза. Если результаты одинаковые — выбираем среднее. Если один или два результата значительно отличаются друг от друга, то запустите тайминг еще несколько раз, пока не получите лучшее представление о том, какие из результатов оказались «левыми». Обратите внимание, некоторые, казалось бы, невинные вещи, такие как веб-браузеры, могут временно увеличить нагрузку на ваш процессор до 100%, когда сайт, на котором вы находитесь в фоновом режиме, выполняет целую кучу скриптов JavaScript (рекламные баннеры, запуск видео, сложная анимация и т.д.). Запуск тайминга несколько раз позволит определить, повлияло ли подобное событие на ваши результаты.

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

Рандомизация также может повлиять на тайминг. Если бы мы отсортировали массив, заполненный случайными числами, то это бы повлияло на результаты тайминга (тот факт, что числа являются рандомными). Рандомизацию использовать можно, но убедитесь, что ваше стартовое значение является фиксированным (т.е. не используйте системные часы в качестве стартового значения) и результаты рандомизации идентичны при каждом запуске. Кроме того, убедитесь, что в фрагментах кода не используется пользовательский ввод, так как время ожидания ввода от пользователя не должно учитываться при определении эффективности кода.

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


Оценить статью:

Звёзд: 1Звёзд: 2Звёзд: 3Звёзд: 4Звёзд: 5 (227 оценок, среднее: 4,88 из 5)
Загрузка...

Комментариев: 12

  1. Дмитрий:

    Добрый день!
    Представленный класс может быть небезопасным с точки зрения синхронизации времени машины между reset() и elapsed(). Можно ли переписать класс Timer, представленный в статье, с использованием std::chrono::steady_clock Он описан, как "is most suitable for measuring intervals."(https://en.cppreference.com/w/cpp/chrono/steady_clock).
    И спасибо огромное за проделанную работу — данный сайт лучшее пособие по C++, которое вообще можно найти в русскоязычном интернете.

  2. zashiki:

    кто подскажет, как chrono сделать многофайлово?
    когда в заголовочном файле класс со всеми функциями — работает
    когда функции переношу из класса в сpp — ругается.
    Походу не переваривает type alias
    как с этим справиться?

    1. Woland:

      Наверное всё дело в корректности переноса функций класса в .cpp

      Пример:

      // Содержимое My.h

      // Содержимое My.cpp

      Удачи!

  3. Woland:

    Ух! Просто в тему попал. Сижу читаю по чуть и учусь на примере. Написал класс для работы с INI файлами. Дописал определение sort() {}. Думаю завтра начну делать, а сегодня ещё почитаю. И надо же так попасть на std::sort(array.begin(), array.end()); За минуту чтения получить готовое решение )).
    Получилось:

    Думал не прокатит, на тип поругается, а нет, прокатило враз.
    сортирую двойной вектор: std::vector<std::vector<std::string>>.

    Спасибочки! Читаем дальше. 😉

  4. Лин:

    Может кому надо:

    Интервалы (duration)
    Моменты (time_point)
    Часы (clock)
    now() – текущий момент времени
    to_time_t() – преобразует момент времени в тип time_t
    from_time_t() – преобразует тип time_t в момент времени системных часов
    Для изменения единиц времени используется формат преобразования в стиле C++ duration_cast<>,

    1. Fandre:

      Спасибо большое;)

  5. Виктор:

    Подскажите, а где можно подробнее почитать про библиотеку chrono. А то в интернете ничего толкового не нашел.

    1. Тарас Программер:

      C++17 STL Яцек Галовиц

  6. Александр:

    Очень полезно, но как я должен догадаться, что есть chrono, что есть now(); и count();?
    Все это гуглением только получится узнать?
    Т.е. передо мной встала задача замерять производительность 2-х кусков кода. Иду в гугл и пишу "время выполнения кода C++"??

  7. alexk:

    минимизировал все что мог:
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    и тем не менее получил вот такие результаты:
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    Сортировка вставками 10000 элементов :
    Потрачено времени : 0.378086

    Сортировка стандартная 10000 элементов :
    Потрачено времени : 0.000680579
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    система программирования:
    g++ (Ubuntu 5.4.0-6ubuntu1~16.04.11) 5.4.0 20160609
    Copyright (C) 2015 Free Software Foundation, Inc.

    Почему у автора перевода сортировка "самопальными" вставками СУЩЕСТВЕННО быстрее ? …
    Неужели только из-за того, что в MSVS целый тип в 2 раза короче(кажется?) ? …

    1. Александр:

      Компьютерное железо тоже играет свою роль. Процессоры так-то по производительности очень сильно различаются.

    2. Евгений Павлов:

      Можно ещё больше оптимизировать используя Си библиотеку time.h.

Добавить комментарий

Ваш E-mail не будет опубликован. Обязательные поля помечены *