C++
C++17

C++ 処理時間計測classを作ってみる

More than 1 year has passed since last update.

はじめに

個人的にたまに使うのでまとめとく

処理時間の計測

std::chronoを使ったシンプルな計測は以下のようにすることができます

main.cpp
#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にしてあげる

ProcessingTime.hpp

    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のインスタンスを生成するだけ

main.cpp
#include"ProcessingTime.hpp"


int main()
{
    {
        ProcessingTime pt("Test");

        /*

        処理

        */

    }
    return 0
}


出力

Test : 0 ms

templateで時間単位を変更可能にしてみる

std::chrono::milliseconds以外の時間単位でも出力できるようにしてみます

ProcessingTime.hpp
    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<>ってかかなくてはいけないのが気に入らなかったので関数を通して生成するようにしてみる

ProcessingTime.hpp
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型しか警告がでないのかな??:thinking::thinking:(でなかった)

使い方は最終的に以下のようになりました。

main.cpp
#include"ProcessingTime.h"

int main()
{
    {
        auto p = ProcessingTime<std::chrono::nanoseconds>("nanosec");

        /*
        処理
        */

    }
    {
        auto p = ProcessingTime("millisec");

        /*
        処理
        */

    }
    return 0;
}