Есть ли функция, которая преобразует наносекунды в
std :: string, сказать что-то вроде "3m12s"?
Нет. Но я покажу вам, как вы можете легко это сделать ниже.
Должен ли я использовать std :: chrono :: stable_clock :: now () каждый раз, когда обновляюсь
мой индикатор выполнения, и вычтите это из _begin, чтобы определить time_left?
Да.
Есть ли лучший алгоритм для определения таймфрейма
Да. Смотри ниже.
Редактировать
Я изначально неверно истолковал «тики» как «такты часов», когда на самом деле «тики» имеют единицы работы и _ticks_occurred/_total_ticks
можно интерпретировать как% job_done. Поэтому я изменил предложенное значение progress_bar
ниже соответственно.
Я считаю, что уравнение:
time_left = (time_taken / _total_ticks) * (_total_ticks - _ticks_occured)
неверно. Он не проходит проверку работоспособности: если _ticks_occured == 1
и _total_ticks
велико, то time_left
приблизительно равно (хорошо, немного меньше) time_taken
. Это не имеет смысла.
Я переписываю вышеприведенное уравнение так:
time_left = time_taken * (1/percent_done - 1)
, где
percent_done = _ticks_occurred/_total_ticks
Теперь, когда percent_done
приближается к нулю, time_left
приближается к бесконечности, а когда percent_done
приближается к 1, 'time_left
приближается к 0. Когда percent_done
равно 10%, time_left
равно 9*time_taken
. Это соответствует моим ожиданиям, принимая во внимание примерно линейную временную стоимость за такт работы.
class progress_bar
{
public:
progress_bar(uint64_t ticks)
: _total_ticks(ticks), _ticks_occurred(0),
_begin(std::chrono::steady_clock::now())
// ...
{}
void tick()
{
using namespace std::chrono;
// test to see if enough progress has elapsed
// to warrant updating the progress bar
// that way we aren't wasting resources printing
// something that hasn't changed
if (/* should we update */)
{
// somehow _ticks_occurred is updated here and is not zero
duration time_taken = Clock::now() - _begin;
float percent_done = (float)_ticks_occurred/_total_ticks;
duration time_left = time_taken * static_cast<rep>(1/percent_done - 1);
minutes minutes_left = duration_cast<minutes>(time_left);
seconds seconds_left = duration_cast<seconds>(time_left - minutes_left);
}
}
private:
typedef std::chrono::steady_clock Clock;
typedef Clock::time_point time_point;
typedef Clock::duration duration;
typedef Clock::rep rep;
std::uint64_t _total_ticks;
std::uint64_t _ticks_occurred;
time_point _begin;
//...
};
Трафик в std :: chrono :: длительностей, когда вы можете. Таким образом <chrono>
сделает все преобразования за вас. typedefs может облегчить ввод с длинными именами. И разбить время на минуты и секунды так же просто, как показано выше.
Как отмечает bames53 в своем ответе, если вы хотите использовать мое <chrono_io>
средство, это тоже круто. Ваши потребности могут быть достаточно простыми, что вы не хотите. Это суждение. Ответ bames53 - хороший ответ. Я подумал, что эти дополнительные детали тоже могут быть полезны.
Редактировать
Я случайно оставил ошибку в коде выше. И вместо того, чтобы просто пропатчить код выше, я подумал, что было бы неплохо указать на ошибку и показать, как использовать <chrono>
для ее исправления.
Ошибка здесь:
duration time_left = time_taken * static_cast<rep>(1/percent_done - 1);
и здесь:
typedef Clock::duration duration;
На практике steady_clock::duration
обычно основывается на целочисленном типе. <chrono>
называет это rep
(сокращение от представление ). И когда percent_done
больше 50%, коэффициент, умноженный на time_taken
, будет меньше 1. А когда rep
является целым числом, это приводит к 0. Так что progress_bar
ведет себя хорошо только первые 50% и прогнозируют 0 времени, оставшегося в течение последних 50%.
Ключом к исправлению этого является трафик в duration
s, основанный на числах с плавающей запятой, а не на целых числах. И <chrono>
делает это очень легко.
typedef std::chrono::steady_clock Clock;
typedef Clock::time_point time_point;
typedef Clock::period period;
typedef std::chrono::duration<float, period> duration;
duration
теперь имеет тот же период тика, что и steady_clock::duration
, но использует float
для представления. И теперь вычисление для time_left
может не включать static_cast:
duration time_left = time_taken * (1/percent_done - 1);
Вот снова весь пакет с этими исправлениями:
class progress_bar
{
public:
progress_bar(uint64_t ticks)
: _total_ticks(ticks), _ticks_occurred(0),
_begin(std::chrono::steady_clock::now())
// ...
{}
void tick()
{
using namespace std::chrono;
// test to see if enough progress has elapsed
// to warrant updating the progress bar
// that way we aren't wasting resources printing
// something that hasn't changed
if (/* should we update */)
{
// somehow _ticks_occurred is updated here and is not zero
duration time_taken = Clock::now() - _begin;
float percent_done = (float)_ticks_occurred/_total_ticks;
duration time_left = time_taken * (1/percent_done - 1);
minutes minutes_left = duration_cast<minutes>(time_left);
seconds seconds_left = duration_cast<seconds>(time_left - minutes_left);
std::cout << minutes_left.count() << "m " << seconds_left.count() << "s\n";
}
}
private:
typedef std::chrono::steady_clock Clock;
typedef Clock::time_point time_point;
typedef Clock::period period;
typedef std::chrono::duration<float, period> duration;
std::uint64_t _total_ticks;
std::uint64_t _ticks_occurred;
time_point _begin;
//...
};
Ничего подобного небольшому тестированию ...; -)