4
\$\begingroup\$

C++ Timeit

A quick C++ library to measure/compare code execution time

Features

  • Easy include and just call timeit() or compareit()
  • Fast It just calculates the average time in the loop
  • Light No lots of code, just two functions are written
  • CrossPlatform It doesn't use any third-party libraries, so it should work anywhere

Syntax

// measure time of function
timeit(count, function, args).unit();   // using any function/arguments type: will return value

// compare time between two functions
compareit(count, function1, function2); // using any function type: will print result
compareit(timeit(..), timeit(..));      // using timeit: will print result

Example 1: measure time of function

void func(int a, int b){ /* ... */ } 
std::cout << timeit(1000, func, 5, 5).nanoseconds() << "[ns]" << std::endl;
// -> 1155144590 [ns]

Example 2: measure time of lambda

std::cout << timeit(1000, []{ /* ... */  }).nanoseconds() << "[ns]" << std::endl;
// -> 1155144590 [ns]

Example 3: compare time between two functions

void func1(){ /* ... */ } 
void func2(){ /* ... */ }
compareit(1000,func1,func2);
// if func1 faster  -> [COMPARE IT] first(1546210342)  > second(452181663) x3.419
// if func2 faster  -> [COMPARE IT] second(1546210342) > first(452181663)  x3.419
// if func1 = func2 -> [COMPARE IT] second(452181663) = first(452181663)

Example 4: compare time between two lambda

compareit(1000,[]{ /* ... */  },[]{ /* ... */  });

Example 5: compare time between two functions using timeit()

void func1(int a, int b){ /* ... */ } 
void func2(int a, int b){ /* ... */ }
compareit(timeit(1000,func1,5,5),timeit(1000,func2,5,5)); // in this case we don't neet to count

Timeit Class Units/Methods

  • hours

  • minutes

  • seconds

  • milliseconds

  • microseconds

  • nanoseconds

  • duration std::chrono::high_resolution_clock::duration

Questions

  • Is there a better way?

  • Are there errors?

  • Any tips?

Source Code

/**
 * @file    timeit.hpp
 * @brief   A quick c++ library to measure/compare code execution time
 * @version 0.4
 *
 * Copyright (c) 2022 Maysara Elshewehy (xeerx.com) ([email protected])
 *
 * Distributed under the MIT License (MIT) 
*/

#pragma once
#include <functional>
#include <chrono>
#include <iostream>

class timeit
{
    // shortcut
    using cloak = std::chrono::high_resolution_clock;

    private: cloak::duration result {};

    public:
    template <class F, typename ...A>
    timeit(unsigned int count, F func, A&& ...args) 
    {
        for (size_t i = 0; i < count; i++)
        {
            auto begin = cloak::now();
            func(std::forward<A>(args)...);
            auto end = cloak::now();
            result += end - begin;
        }

        result = result / count;
    }

    auto nanoseconds () { return result.count();          }
    auto microseconds() { return nanoseconds   () / 1000; }
    auto milliseconds() { return microseconds  () / 1000; }
    auto seconds     () { return milliseconds  () / 1000; }
    auto minutes     () { return seconds       () / 60;   }
    auto hours       () { return minutes       () / 60;   }

    auto duration    () { return result;                  }

    timeit(const timeit &) = default;
    timeit(     timeit &&) = default;
    ~timeit             () = default;
};

class compareit
{
    public:
    template <class F>
    compareit(unsigned int count, F func1, F func2) 
    {
        unsigned long int t1 = timeit(count, func1).nanoseconds();
        unsigned long int  t2 = timeit(count, func2).nanoseconds();
        handler(count,t1,t2);
    }
    compareit(unsigned int count, timeit func1, timeit func2) 
    {
        handler(count,func1.nanoseconds(),func2.nanoseconds());
    }

    void handler(unsigned int count, unsigned long int  t1, unsigned long int t2)
    {
        unsigned long int max = 0, min = 0;
        if(t1 > t2) { max = t1; min = t2; } else if(t1 < t2) { max = t2; min = t1; } 
        else 
        {
            std::cout 
            << "\033[1;33m" << "[COMPARE IT]" << "\033[0m"
            << "\033[0;34m" << " first(" << "\033[1;35m" << t1 << "\033[0;34m" << ") " << "\033[0m"
            << "\033[1;33m" << "=" << "\033[0m"
            << "\033[0;34m" << " second(" << "\033[1;35m" << t2 << "\033[0;34m" << ") " << "\033[0m" << std::endl;
            return;
        }
        
        auto tot = (max + min);
        auto diff = (max - min);
        // auto percent = (static_cast<long double>(diff) / tot) * 100;
        auto percent = (static_cast<long double>(max) / min);
        
        bool t1Max = max == t1 ? true : false;

        std::cout 
        << "\033[1;33m" << "[COMPARE IT]" << "\033[0m"
        << "\033[0;34m" << (t1Max ? " first(" : " second(") << "\033[1;35m" << (t1Max? t1 : t2) << "\033[0;34m" << ") " << "\033[0m"
        << "\033[1;33m" << ">" << "\033[0m"
        << "\033[0;34m" << (t1Max ? " second(" : " first(")  << "\033[1;35m" << (t1Max ? t2 : t1) << "\033[0;34m" << ") " << "\033[0m"
        << "\033[1;31m" << "x" << std::fixed << std::setprecision(3) << percent << "\033[0m" << std::endl;
    }

    compareit(const compareit &) = default;
    compareit(     compareit &&) = default;
    ~compareit                () = default;
};

GitHub

\$\endgroup\$
5
  • \$\begingroup\$ Curious: cloak? \$\endgroup\$
    – L. F.
    Commented Jul 5, 2022 at 2:43
  • \$\begingroup\$ ohh !.......... \$\endgroup\$ Commented Jul 5, 2022 at 3:33
  • 2
    \$\begingroup\$ I have rolled back your last edit. Please do not update the code in your question to incorporate feedback from answers because this invalidates the answer and goes against the Q&A format of this site. This is not a forum where you should keep the most updated version in your question. Please see what you may and may not do after receiving answers. \$\endgroup\$
    – L. F.
    Commented Jul 5, 2022 at 3:37
  • 1
    \$\begingroup\$ Please note that posting code on Code Review also licences it under CC BY-SA 4.0, regardless of your note about MIT. \$\endgroup\$
    – Mast
    Commented Jul 5, 2022 at 5:07
  • \$\begingroup\$ Related question. \$\endgroup\$
    – Mast
    Commented Jul 5, 2022 at 5:09

2 Answers 2

3
\$\begingroup\$

In the constructor, result = result / count can be rewritten as result /= count. The code mixes unsigned int and size_t (which, by the way, should be std::size_t with #include <cstddef>); better stick to one. Perfect forwarding is inappropriate here as the function may be called multiple times, and moving from the same arguments more than once makes no sense, so we have to stick to lvalues here:

// the arguments can still be captured by universal reference
template <typename F, typename... Args>
explicit timeit(std::size_t count, F f, Args&&... args)
{
    for (std::size_t i = 0; i < count; ++i) {
        // ...

        // but the function can only be called with lvalues
        f(args...);

        // ...
    }

    // ...
}

The user should wrap the function call in a lambda if rvalue arguments have to be used (although this causes the construction of arguments to be included in the timing under the current API). Also, consider testing for count == 0 and throwing an exception (instead of dividing by zero).

Do not explicitly default the special member functions if customization isn't needed. In fact, the current implementation unnecessarily deletes the copy and move assignment operators.

The methods nanoseconds, microseconds, etc. can be unified using std::chrono::duration_cast:

// note the `const`
template <typename ToDuration>
ToDuration as() const
{
    return std::chrono::duration_cast<ToDuration>(result);
}

.nanoseconds() can then be .as<std::chrono::nanoseconds>(), and similarly for the others.

std::minmax can be used in handler. std::setprecision needs #include <iomanip>. Also consider using '\n' instead of std::endl unless flushing is necessary.

\$\endgroup\$
0
0
\$\begingroup\$

[ NEW VERSION ]

Features

  • High Resolution using a perfect algorithm to get the best result
  • Easy include and just call timeit() or compareit()
  • Fast It just loop to find the median result
  • Light No lots of code, just simple little functions
  • CrossPlatform It doesn't using any third party libraries, so it should work anywhere

Syntax

// measure time of function
timeit(count, function, args).unit();   // return value

// compare time between two functions
compareit(count, function1, function2); // print result
compareit(timeit(..), timeit(..));      // print result

// repeat function
repeatit(count, function, args);

High Resolution

timeit()

// simple function to test
void func() { for (auto i = 0; i < 10; i++) sqrt(i); }

// repeat `timeit()` 3 times with same values
repeatit(3,[]{ std::cout << timeit(1000, func).nanoseconds() << std::endl; });

result

22
22 
22

compareit()

// simple functions to test
void func1() { for (auto i = 0; i < 1000; i++) sqrt(i); }
void func2() { for (auto i = 0; i < 10; i++)   sqrt(i); }

// repeat `compareit()` 3 times with same values
repeatit(3,[]{ compareit(1000,func1,func2); });

result

[COMPARE IT] first(675)  > second(22) x30
[COMPARE IT] first(697)  > second(22) x31
[COMPARE IT] first(698)  > second(23) x30

SourceCode

\$\endgroup\$
1
  • \$\begingroup\$ Rather than posting an answer with updated code, you can ask a follow up question with a link back to this question. \$\endgroup\$
    – pacmaninbw
    Commented Jul 12, 2022 at 13:25

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.