Search code examples
c++timestdc++-chrono

C++ use std::chrono to measure execution of member functions in a nice way


I want to optimize my application, especially the execution speed of certain functions.

Imagine there is a class with some member functions

class Test
{
public:
    Test();
    virtual ~Test();
    int init(int arg1, double arg2);

private:
    [...]

and in my constructor I call one of these methods

Test::Test()
{
    [...]
    int value = init(1, 1.2);
}

How can I measure the execution time of my method init(...) in a nice and clean way without breaking my program?

At the moment I use following code

Test::Test()
{
    [...]
    auto start = std::chrono::high_resolution_clock::now();

    int value = init(1, 1.2);

    auto stop = std::chrono::high_resolution_clock::now();
    std::chrono::duration<double> duration = stop - start;
    std::cout << duration.count() * 1000 << "ms\n";
}

It works as expected but I think it is pretty messy and I want to have a "cleaner" solution.

Is there a way to have some kind of function which takes a member function and other parameters like so

int value = countTime(function, arg1, arg2);

I don't know whether it is possible to pass the return value from function() to countTime() in order to don't interrupt the workflow of my code.

EDIT: This is my TimeMeasure class

namespace tools 
{
    class TimeMeasure 
    {
    public:
        TimeMeasure() 
        {
            m_start = std::chrono::high_resolution_clock::now();
        }

        virtual ~TimeMeasure()
        {
            m_stop = std::chrono::high_resolution_clock::now();
            std::chrono::duration<double, std::milli> duration = m_stop - m_start;
            std::cout << duration.count() << "ms\n";
        }

    public:
        typedef std::chrono::time_point<std::chrono::high_resolution_clock> HighResClock;

    private:
        HighResClock m_start;
        HighResClock m_stop;
    };

    template <typename T, typename F, typename... Args>
    auto measure(T *t, F &&fn, Args... args)
    {
        tools::TimeMeasure timeMeasure;
        return (t->*fn)(std::forward<Args>(args)...);
    }
}

and in my constructor Test() I use the function measure this way

Test()
{
    [...]
    tools::measure(this, Test::init, filepath);
}

int init(const std::string& filepath) const takes here a string to a file. So in my case it's just one argument

Unfortunately I get a invalid use of non-static member function 'int init(const string&) const' error

I would wonder if a constructor is not a member function. So why do I get this error?

EDIT 2:

According to OznOg's answer I just forgot to hand in a pointer to my function.

So this would be the correct function call

tools::measure(this, &Test::init, filepath);

Solution

  • You could create a class like:

    struct MeasureTime {
        MeasureTime() : _start(std::chrono::high_resolution_clock::now()) {}
    
        ~MeasureTime() {
            auto stop = std::chrono::high_resolution_clock::now();
            std::chrono::duration<double> duration = stop - _start;
            std::cout << duration.count() * 1000 << "ms\n";
        }
    private:
        std::chrono::time_point<std::chrono::high_resolution_clock>  _start;
    };
    

    and simply use it in your code:

    Test::Test()
    {
        MeasureTime mt;
        [...]
        { //or even this for just the init call
        MeasureTime mt2;
        int value = init(1, 1.2);
        }
    }
    

    IMHO it is less intrusive than what you proposed.

    If you really want a function, you may try a wrapper like:

    template <class T, class F, class... Args>
    auto MeasureTimeFn(T *t, F &&fn, Args&&... args) {
        MeasureTime timer;
         return (t->*fn)(std::forward<Args>(args)...);
    }
    

    And call it like:

    int value = MeasureTimeFn(this, &Test::init, 1, 1.2);
    

    but not sure it is really much better.

    You can try to hide thing with a macro:

    #define MEASURE(f, ...) \
      MeasureTimeFn(this, &std::remove_reference_t<decltype(*this)>::f, __VA_ARGS__)
    

    this way you can write

    int value = MEASURE(init, 1, 1.2);
    

    what is quite like what you asked for, but only works inside member functions, with member functions (non static).

    Anyway probably a good place to start with.

    * EDIT* If you can modify inheritance of you class, you may try

    template<class T>
    struct MeasureTool {
        template <class F, class... Args>
        auto measure(F &&fn, Args&&... args) {
            tools::TimeMeasure timeMeasure;
            return (static_cast<T*>(this)->*fn)(std::forward<Args>(args)...);
        }
    };
    
    class Test : public MeasureTool<Test>
    {
    public:
        Test();
        virtual ~Test() {}
        int init(const std::string &filepath) { _path = filepath; return 0; }
        const auto &getPath() const { return _path; }
    private:
        std::string _path;
    
    };
    
    Test::Test()
    {
        std::string filepath("/some/where");
        int value = measure(&Test::init, filepath);
        measure(&Test::getPath);
    }
    

    And, this time, seems to match your very first requirement (but is quite intrusive...)

    now, it's all in your hands :)