#はじめに
個人的にたまに使うのでまとめとく
#処理時間の計測
std::chrono
を使ったシンプルな計測は以下のようにすることができます
#include<iostream>
#include<chrono>
int main()
{
std::chrono::system_clock::time_point start,end;
start = std::chrono::system_clock::now();
/*
処理
*/
end = std::chrono::system_clock::now();
auto elapsed = std::chrono::duration_cast< std::chrono::milliseconds >(end - start).count();
std::cout << elapsed <<"ms"<< std::endl;
return 0;
}
std::chrono::milliseconds
の所を変えれば時間単位を変えれます
型 | 単位 |
---|---|
nanoseconds | ナノ秒 |
microseconds | マイクロ秒 |
milliseconds | ミリ秒 |
seconds | 秒 |
minutes | 分 |
hours | 時 |
#classにしてみる
先ほどの処理を毎回書くのは大変なのでclassにしてあげる
class ProcessingTime
{
public:
ProcessingTime(const std::string& name = "Process", bool start = true) :
m_name(name),
m_isActive(start)
{
if (start)
{
this->restart();
}
}
~ProcessingTime()
{
this->stop();
}
///<summary>
///計測のリスタート
///</summary>
void restart()&
{
m_start = std::chrono::system_clock::now();
m_isActive = true;
}
///<summary>
///計測を終了し出力
///</summary>
void stop()&
{
if (!m_isActive)
return;
const auto end = std::chrono::system_clock::now();
const auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds >(end - m_start).count();
std::cout << elapsed <<"ms"<< std::endl;
m_isActive = false;
}
private:
std::string m_name;
std::chrono::system_clock::time_point m_start;
bool m_isActive;
};
コンストラクタで計測開始、デストラクタで計測終了を呼べるようにしてます
使い方は計測したい箇所をスコープで括って最初にProcessingTimeのインスタンスを生成するだけ
#include"ProcessingTime.hpp"
int main()
{
{
ProcessingTime pt("Test");
/*
処理
*/
}
return 0
}
出力
Test : 0 ms
#templateで時間単位を変更可能にしてみる
std::chrono::milliseconds
以外の時間単位でも出力できるようにしてみます
template<class T>
struct is_duration :std::false_type
{};
template<class Rep, class Period>
struct is_duration<std::chrono::duration<Rep, Period>> :std::true_type
{};
template<class Duration>
class ProcessingTime
{
static_assert(is_duration<Duration>::value, "tempate parameter requires std::chrono::duration");
public:
//省略
void stop()&
{
if (!m_isActive)
return;
const auto end = std::chrono::system_clock::now();
const auto elapsed = std::chrono::duration_cast<Duration>(end - m_start).count();
std::cout << m_name << " : " << elapsed << " " << this->getUnit() << std::endl;
m_isActive = false;
}
private:
std::string getUnit()const
{
if constexpr(std::is_same_v<Duration, std::chrono::nanoseconds>)return "ns";
if constexpr(std::is_same_v<Duration, std::chrono::microseconds>)return "us";
if constexpr(std::is_same_v<Duration, std::chrono::milliseconds>)return "ms";
if constexpr(std::is_same_v<Duration, std::chrono::seconds>)return "s";
if constexpr(std::is_same_v<Duration, std::chrono::minutes>)return "min";
if constexpr(std::is_same_v<Duration, std::chrono::hours>)return "h";
return " ";
}
std::string m_name;
std::chrono::system_clock::time_point m_start;
bool m_isActive;
};
メタ関数is_duration
を作ってstd::chrono::duration
以外の型はアサートするようにしてみました
また、それぞれの型の時間単位をC++17から導入されたif constexpr
でコンパイル時に返り値を確定できるようにしてみました
#少し修正
クラステンプレートの場合、template引数のデフォルトを用意してもProcessingTime<>ってかかなくてはいけないのが気に入らなかったので関数を通して生成するようにしてみる
namespace detail
{
template<class T>
struct is_duration :std::false_type
{};
template<class Rep, class Period>
struct is_duration<std::chrono::duration<Rep, Period>> :std::true_type
{};
///<summary>
///処理計測実装
///</summary>
template<class Duration>
class ProcessingTime_impl
{
static_assert(is_duration<Duration>::value, "tempate parameter requires std::chrono::duration");
public:
ProcessingTime_impl(const std::string& name = "Process", bool start = true) :
m_name(name),
m_isActive(start)
{
if (start)
{
this->restart();
}
}
~ProcessingTime_impl()
{
this->stop();
}
///<summary>
///計測のリスタート
///</summary>
void restart()&
{
m_start = std::chrono::system_clock::now();
m_isActive = true;
}
///<summary>
///計測を終了し出力
///</summary>
void stop()&
{
if (!m_isActive)
return;
const auto end = std::chrono::system_clock::now();
const auto elapsed = std::chrono::duration_cast<Duration>(end - m_start).count();
std::cout << m_name << " : " << elapsed << " " << this->getUnit() << std::endl;
m_isActive = false;
}
private:
std::string getUnit()const
{
if constexpr(std::is_same_v<Duration, std::chrono::nanoseconds>)return "ns";
if constexpr(std::is_same_v<Duration, std::chrono::microseconds>)return "us";
if constexpr(std::is_same_v<Duration, std::chrono::milliseconds>)return "ms";
if constexpr(std::is_same_v<Duration, std::chrono::seconds>)return "s";
if constexpr(std::is_same_v<Duration, std::chrono::minutes>)return "min";
if constexpr(std::is_same_v<Duration, std::chrono::hours>)return "h";
return " ";
}
std::string m_name;
std::chrono::system_clock::time_point m_start;
bool m_isActive;
};
}
///<summary>
///処理時間の計測
///</summary>
///<param name="name">
///処理名
///</param>
///<param name="start">
///すぐに計測を開始するか? true=>開始
///</param>
template<
class Duration = std::chrono::milliseconds,
std::enable_if_t<detail::is_duration<Duration>::value>* = nullptr
>
[[nodiscard]] detail::ProcessingTime_impl<Duration> ProcessingTime(const std::string& name = "Process", bool start = true)
{
return detail::ProcessingTime_impl<Duration>(name, start);
}
せっかくなので[[nodiscard]]
属性で帰り値を破棄した場合に警告をだすようにしてみたが msvcはpod型しか警告がでないのかな??(でなかった)
使い方は最終的に以下のようになりました。
#include"ProcessingTime.h"
int main()
{
{
auto p = ProcessingTime<std::chrono::nanoseconds>("nanosec");
/*
処理
*/
}
{
auto p = ProcessingTime("millisec");
/*
処理
*/
}
return 0;
}