使用包装器确定函数时间

Determining function time using a wrapper

我正在寻找一种通用的方法来测量像 这样的函数时序,但对于 c++。

我的主要目标是不要到处都是这样的混乱代码:

  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);

而是对该函数进行一个很好的包装。 到目前为止我得到的是:

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;
}

如果有人想测试代码,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);
        }
    }
};

问题是,由于 std::function 用法,这段代码仍然很难看。有更好的 and/or 更简单的选择吗? (+ 我确定我用 std::bind 搞砸了某事,我想我需要使用 std::placeholders 因为我想稍后设置参数。)

// 编辑,正确使用 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 );

您可以利用 RAII 实现一个记录代码块执行时间的计时器和一个模板函数,该模板函数包装您要使用计时器执行的函数。

#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));
}

标准输出:

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

总体思路:

  1. time_fn 是一个简单的模板函数,它调用 START_BENCHMARK_TIMER 并使用提供的参数
  2. 调用 fn
  3. START_BENCHMARK_TIMER 然后创建一个 Timer 对象。它将在start中记录当前时间。请注意,__FUNCTION__ 将替换为调用的函数。
  4. 当 如果 fn returns 或抛出异常,(1) 中的 Timer 对象将被销毁并调用析构函数。然后析构函数会计算当前时间和记录的start时间的时间差,打印到stdout

注:

  • 即使在 time_fn 中声明 startend 而不是 RAII 计时器也可以工作,拥有 RAII 计时器将允许您干净地处理 [=14= 时的情况] 抛出异常
  • 如果您使用的是 c++11,则需要将 time_fn 声明更改为 typename std::result_of<F &&(Args &&...)>::type time_fn(F&& fn, Args&&... args)

编辑:更新了响应以包含包装函数方法。