бенчмаркинг — установка / приостановка времени часов для std :: chrono clock в переполнении стека

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

Дело в том, что в процессе получения временного интервала, преобразования его в число, получения формата времени, а затем преобразования всего в строку и последующей ее распечатки требуется некоторое время (2-3 миллисекунды), особенно ввод / вывод кажется дорогим. Я хочу, чтобы часы «пропустили» этот процесс в некотором смысле, так как измеряемый материал будет в микросекундах. (и я думаю, что это будет хорошая функция, если у меня есть другие вещи, которые я хочу пропустить)

std::chrono::high_resolution_clock clock;
std::chrono::time_point<std::chrono::steady_clock> first, second;

first = clock.now();

std::chrono::time_point<std::chrono::high_resolution_clock> paused_tp = clock.now();
std::cout << "Printing things \n";
clock.setTime(paused_tp); // Something like this is what I want

second = clock.now();

Идея состоит в том, чтобы убедиться, что first а также second иметь минимальные различия, идеально идентичные.

Из того, что я вижу, high_resolution_clock класс (и все остальные chrono часы), держи их time_point частный, и вы можете получить к нему доступ только с clock.now()

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

(Является std::chrono::high_resolution_clock даже достаточно точно для чего-то подобного?)

(Хотя я здесь, любые ресурсы по повышению эффективности программ на C ++ были бы хорошими)

Редактировать: Я на самом деле печатаю после фрагмента кода, который пытаюсь рассчитать, проблема возникает, например, в том случае, когда я хочу синхронизировать всю программу, а также отдельные функции. Тогда печать времени функции приведет к задержке общего времени программы.

Редактировать 2: Я подумал, что мне нужно больше примеров того, что я делаю.
У меня есть класс, который обрабатывает все, скажем, он называется trackerОн заботится обо всех этих глупостях.

tracker loop = TRACK(
for(int i = 0; i != 100; ++i){
tracker b = TRACK(function_call());
b.display();
}
)
loop.display();

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

Явно макрос расширяется до

tracker loop = "for(int i = 0; i != 100; ++i){ tracker b = TRACK(function_call()); b.display(); }"loop.start()
for(int i = 0; i != 100; ++i){
tracker b = "function_call()"b.start();
function_call();
b.end();
b.display();
}
loop.end();
loop.display();

В большинстве случаев печать не является проблемой, она только отслеживает, что между start() а также end(), но здесь b.display() в конечном итоге вмешивается в tracker loop,

Моя цель с этим состояла в том, чтобы трекер был как можно более ненавязчивым, поэтому я бы хотел, чтобы большинство / все это обрабатывалось в классе трекера. Но потом я сталкиваюсь с проблемой b.display() будучи методом другого экземпляра, чем tracker loop, Я попробовал несколько вещей с static ключевое слово, но столкнулся с несколькими проблемами с этим (все еще пробуя немного). Возможно, я застрял здесь в тупике, но еще многое предстоит попробовать.

2

Решение

Просто выделите два интервала отдельно и добавьте их, т.е. сохранить 4 всего времени. Для вложенных интервалов вы можете просто сохранить метки времени в массиве и отсортировать все в конце. (Или внутри внешнего цикла до того, как метки времени будут перезаписаны). Хранение в массиве довольно дешево.


Или лучше: отложите печать на потом.

Если временной интервал составляет всего миллисекунды, просто сохраните то, что вы собирались распечатать, и сделайте это вне временного интервала.

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

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


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

(См. Раздел «(Реальные) затраты на системные вызовы» в документе FlexSC (Soares, Stumm), приурочен к i7 Nehalem под управлением Linux. (Первый поколения i7 из ~ 2008/9). В статье предлагается пакетный механизм системных вызовов для высокопроизводительных веб-серверов и тому подобное, но их базовые результаты для простого Linux интересны и актуальны вне этого.)

На современном процессоре Intel с включенным смягчением Meltdown вы, как правило, будете получать пропуски TLB. Если в недавнем выпуске x86 включено смягчение спектра, история предсказаний ветвлений, вероятно, будет уничтожена, в зависимости от стратегии смягчения. (Intel добавила в ядро ​​способ запрашивать, чтобы ветви с более высокими привилегиями после этой точки не влияли на историю предсказаний для ветвей с более низкими привилегиями. Я думаю, что на современных процессорах это просто сбрасывает кэш предсказания ветвлений.)

Вы можете избежать издержек системного вызова, позволяя iostream буфер для вас. Это все еще значительная работа форматирование и копирование данных вокруг, но много дешевле, чем писать в терминал. Перенаправление вашей программы stdout в файл сделаю cout полный буферизованный по умолчанию, а не линейный. т.е. запустить его так:

 ./my_program > time_log.txt

Окончательный результат будет соответствовать тому, что вы получили бы на терминале, но (до тех пор, пока вы не делаете ничего глупого, как использование std::endl вызвать флеш), он просто будет забуферен. Размер буфера по умолчанию, вероятно, что-то вроде 4kiB. использование strace ./my_program или аналогичный инструмент для отслеживания системных вызовов, и убедитесь, что вы получаете один большой write() в конце вместо множества маленьких write()s.

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

cout << foo если это не делает системный вызов, не является «особенным» с точки зрения замедления последующего кода.

1

Другие решения

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

#include<iostream>
#include<thread>
#include<chrono>
#include<mutex>
#include<condition_variable>
#include<atomic>

std::condition_variable cv;
std::mutex mu;
std::atomic<bool> running {true};
std::atomic<bool> printnow {false};

// shared but non-atomic: protected by the mutex and condition variable.
// possible bug: the main thread can write `now` before print_thread wakes up and reads it
std::chrono::high_resolution_clock::time_point start;
std::chrono::high_resolution_clock::time_point now;void print_thread() {
std::thread([]() {
while (running) {
std::unique_lock<std::mutex> lock(mu);
cv.wait(lock, []() { return !running || printnow; });
if (!running) return;
std::chrono::milliseconds lapse_ms = std::chrono::duration_cast<std::chrono::milliseconds>(now - start);
printnow = false;
std::cout << " lapse time " << lapse_ms.count() << "  ms\n";
}
}).detach();
}

void print_lapse(std::chrono::high_resolution_clock::time_point start1, std::chrono::high_resolution_clock::time_point now1) {
start = start1;
now = now1;
printnow = true;
cv.notify_one();
}

int main()
{
//launch thread
print_thread();

// laspe1
std::chrono::high_resolution_clock::time_point start1 = std::chrono::high_resolution_clock::now();
std::this_thread::sleep_for(std::chrono::milliseconds(200));
std::chrono::high_resolution_clock::time_point now1 = std::chrono::high_resolution_clock::now();
print_lapse(start1,now1);

// laspe2
start1 = std::chrono::high_resolution_clock::now();
std::this_thread::sleep_for(std::chrono::milliseconds(500));
now1 = std::chrono::high_resolution_clock::now();
print_lapse(start1, now1);

//winding up
std::this_thread::sleep_for(std::chrono::milliseconds(300));
running = false;
cv.notify_one();
}
0

По вопросам рекламы [email protected]