Home > Software engineering >  Determining function time using a wrapper
Determining function time using a wrapper

Time:02-11

I'm looking for a generic way of measuring a functions timing like Here, but for c .

My main goal is to not have cluttered code like this piece everywhere:

  auto t1 = std::chrono::high_resolution_clock::now();
  function(arg1, arg2);
  auto t2 = std::chrono::high_resolution_clock::now();
  auto tDur = std::chrono::duration_cast<std::chrono::microseconds>(t2 - t1);

But rather have a nice wrapper around the function. What I got so far is:

timing.hpp:

#pragma once

#include <chrono>
#include <functional>

template <typename Tret, typename Tin1, typename Tin2> unsigned int getDuration(std::function<Tret(Tin1, Tin2)> function, Tin1 arg1, Tin2 arg2, Tret& retValue)
{
  auto t1 = std::chrono::high_resolution_clock::now();
  retValue = function(arg1, arg2);
  auto t2 = std::chrono::high_resolution_clock::now();
  auto tDur = std::chrono::duration_cast<std::chrono::microseconds>(t2 - t1);
  return tDur.count();
}

main.cpp:

#include "timing.hpp"
#include "matrix.hpp"

constexpr int G_MATRIXSIZE = 2000;

int main(int argc, char** argv)
{
CMatrix<double> myMatrix(G_MATRIXSIZE);

bool ret;
// this call is quite ugly
std::function<bool(int, std::vector<double>)> fillRow = std::bind(&CMatrix<double>::fillRow, &myMatrix, 0, fillVec);
auto duration = getDuration(fillRow, 5, fillVec, ret );
std::cout << "duration(ms): " << duration << std::endl;
}

in case sb wants to test the code, matrix.hpp:

#pragma once

#include <iostream>
#include <string>
#include <sstream>
#include <vector>

template<typename T> class CMatrix {
public:
    // ctor
    CMatrix(int size) :
        m_size(size)
    {
        m_matrixData = new std::vector<std::vector<T>>;
        createUnityMatrix();
    }
    // dtor
    ~CMatrix()
    {
        std::cout << "Destructor of CMatrix called" << std::endl;
        delete m_matrixData;
    }
    
    // print to std::out
    void printMatrix()
    {
        std::ostringstream oss;
        for (int i = 0; i < m_size; i  )
        {
            for (int j = 0; j < m_size; j  )
            {
                oss << m_matrixData->at(i).at(j) << ";";
            }
            oss << "\n";
        }
        std::cout << oss.str() << std::endl;
    }

    bool fillRow(int index, std::vector<T> row)
    {
        // checks
        if (!indexValid(index))
        {
            return false;
        }

        if (row.size() != m_size)
        {
            return false;
        }

        // data replacement
        for (int j = 0; j < m_size; j  )
        {
            m_matrixData->at(index).at(j) = row.at(j);
        }
        return true;
    }

    bool fillColumn(int index, std::vector<T> column)
    {
        // checks
        if (!indexValid(index))
        {
            return false;
        }

        if (column.size() != m_size)
        {
            return false;
        }

        // data replacement
        for (int j = 0; j < m_size; j  )
        {
            m_matrixData->at(index).at(j) = column.at(j);
        }
        return true;
    }

private:
    // variables
    std::vector<std::vector<T>>* m_matrixData;
    int m_size;

    bool indexValid(int index)
    {
        if (index   1 > m_size)
        {
            return false;
        }
        return true;
    }

    // functions
    void createUnityMatrix()
    {
        for (int i = 0; i < m_size; i  )
        {
            std::vector<T> _vector;
            for (int j = 0; j < m_size; j  )
            {
                if (i == j)
                {
                    _vector.push_back(1);
                }
                else
                {
                    _vector.push_back(0);
                }
            }
            m_matrixData->push_back(_vector);
        }
    }
};

The thing is, this code is still quite ugly due to the std::function usage. Is there a better and/or simpler option ? ( also I'm sure I messed sth up with the std::bind, I think I need to use std::placeholders since I want to set the arguments later on.)

// edit, correct use of placeholder in main:

std::function<bool(int, std::vector<double>)> fillRow = std::bind(&CMatrix<double>::fillRow, &myMatrix, std::placeholders::_1, std::placeholders::_2);
  auto duration = getDuration(fillRow, 18, fillVec, ret );

CodePudding user response:

You can utilize RAII to implement a timer that records the execution time of a code block and a template function that wraps the function you would like to execute with the timer.

#include<string>
#include<chrono>
#include <unistd.h>

struct Timer
{
    std::string fn, title;
    std::chrono::time_point<std::chrono::steady_clock> start;
    Timer(std::string fn, std::string title)
        : fn(std::move(fn)), title(std::move(title)), start(std::chrono::steady_clock::now())
    {
    }
    ~Timer()
    {
        const auto elapsed =
            std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now() - start).count();
        printf("%s: function=%s; elasepd=%f ms\n", title.c_str(), fn.c_str(), elapsed / 1000.0);
    }
};

#ifndef ENABLE_BENCHMARK
static constexpr inline void dummy_fn() { }
#define START_BENCHMARK_TIMER(...) dummy_fn()
#else
#define START_BENCHMARK_TIMER(title) bench::Timer timer(__FUNCTION__, title)
#endif

template<typename F, typename ...Args>
auto time_fn(F&& fn, Args&&... args) {
  START_BENCHMARK_TIMER("wrapped fn");
  return fn(std::forward<Args>(args)...);
}

int foo(int i) {
  usleep(70000);
  return i;
}

int main()
{
    printf("%d\n", time_fn(foo, 3));
}

stdout:

wrapped fn: function=time_fn; elasepd=71.785000 ms
3

General Idea:

  1. time_fn is a simple template function that calls START_BENCHMARK_TIMER and calls fn with the provided arguments
  2. START_BENCHMARK_TIMER then creates a Timer object. It will record the current time in start. Do note that __FUNCTION__ will be replaced with the function that was called.
  3. When the provided fn returns or throws an exception, the Timer object from (1) will be destroyed and the destructor will be called. The destructor will then calculate the time difference between the current time and the recorded start time and prints it to stdout

Note:

  • Even though declaring start and end in time_fn instead of the RAII timer will work, having an RAII timer will allow you to cleanly handle the situation when fn throws an exception
  • If you are on c 11, you will need to change time_fn declaration to typename std::result_of<F &&(Args &&...)>::type time_fn(F&& fn, Args&&... args).

Edit: Updated the response to include a wrapper function approach.

  • Related