当我最终使用这种结构时,我花了很多时间来弄清楚。

#include <iostream>
#include <chrono>

template<typename TimeT = std::chrono::milliseconds>
struct measure
{
    template<typename F>
    static typename TimeT::rep execution(F const &func)
    {
        auto start = std::chrono::system_clock::now();
        func();
        auto duration = std::chrono::duration_cast< TimeT>(
            std::chrono::system_clock::now() - start);
        return duration.count();
    }
};


用例是

struct functor
{
    int state;
    functor(int state) : state(state) {}
    void operator()() const
    {
        std::cout << "In functor run for ";
    }
};

void func()
{
    std::cout << "In function, run for " << std::endl;
}

void func(int arg) {}

int main()
{
    int dummy(1);

    std::cout << measure<>::execution( [&]() {  
        func(dummy);
    }) << std::endl;

    std::cout << measure<>::execution(functor(dummy)) << std::endl;
std::cout << measure<>::execution(func);

    return 0;
}


但是自从我在Stack Exchange网站上的各种帖子中开始使用struct以来,我就对代码进行了评论和评论(例如func上是否为const ref等)。

我想对上述内容进行评论,以及您对我如何批准此代码(或最后不予理会)的建议。

评论

您的课程不适用于需要参数的函数。应该是这样吗?

它会起作用,只需通过lambda调用该函数

@ NikosAthanasiou是的,那是可能的。但是,由于您也正在测量另一个函数的执行时间,因此测量将异相。

@black Lambda函数总是内联的,因此代码被调用到位。此外,对于要进行的任何测量,单个函数调用(如前所述,将被内联)都不重要。

@NikosAthanasiou如您所愿。但请注意,这样做既不合逻辑,也不期望这样做。

#1 楼


扩展计时器功能,使其可以获取func()所需的所有参数

不必麻烦通过const引用传递func

我应该做的
/>
template<typename TimeT = std::chrono::milliseconds>
struct measure
{
    template<typename F, typename ...Args>
    static typename TimeT::rep execution(F func, Args&&... args)
    {
        auto start = std::chrono::system_clock::now();

        // Now call the function with all the parameters you need.
        func(std::forward<Args>(args)...);

        auto duration = std::chrono::duration_cast< TimeT>(std::chrono::system_clock::now() - start);

        return duration.count();
    }
};


评论


\ $ \ begingroup \ $
+1。这些可变参数模板解决了很多问题!您会推荐我可以了解更多信息的资源吗? (请不要说标准)
\ $ \ endgroup \ $
– Nikos Athanasiou
2014年5月3日19:34

\ $ \ begingroup \ $
@NikosAthanasiou:这个人似乎知道他在说什么。 codereview.stackexchange.com/users/39083/iavr阅读了一些答案
\ $ \ endgroup \ $
–马丁·约克
2014年5月3日19:36

\ $ \ begingroup \ $
快速提问。为什么不能编译原始示例?我猜想是由于重载func,但是对此我有什么想法吗?
\ $ \ endgroup \ $
– Nikos Athanasiou
2014年5月3日19:52

\ $ \ begingroup \ $
@NikosAthanasiou:这是因为您有两个名为func()的函数。它不知道您要测量的意思是<> :: execution(func); (可以是)。您可以使用不同的名称命名功能,也可以在指定功能时指定特定名称,方法是提供以下类型的功能:measure <> :: execution (func);。
\ $ \ endgroup \ $
–马丁·约克
2014年5月3日23:20



\ $ \ begingroup \ $
我对SO帖子进行了改进:可以转发函数调用,因此可以对func使用通用引用。
\ $ \ endgroup \ $
– Nikos Athanasiou
15年8月28日在12:00

#2 楼

当我觉得它更像一个对象时,将它作为函数模板来处理似乎有点麻烦。因此,可以使用一种更自然的选择:

template<typename TimeT = std::chrono::microseconds, 
    typename ClockT=std::chrono::high_resolution_clock,
    typename DurationT=double>
class Stopwatch
{
private:
    std::chrono::time_point<ClockT> _start, _end;
public:
    Stopwatch() { start(); }
    void start() { _start = _end = ClockT::now(); }
    DurationT stop() { _end = ClockT::now(); return elapsed();}
    DurationT elapsed() { 
        auto delta = std::chrono::duration_cast<TimeT>(_end-_start);
        return delta.count(); 
    }
};


然后您可以像这样使用它:

Stopwatch<> sw;
functionToBeTimed(arg1, arg2);
sw.stop();
std::cout << "functionToBeTimed took " << sw.elapsed() << " microseconds\n";


评论


\ $ \ begingroup \ $
不自然。但这具有可以对多个函数调用计时的优点。
\ $ \ endgroup \ $
–马丁·约克
2014年5月3日23:29

\ $ \ begingroup \ $
您还可以为非功能计时。
\ $ \ endgroup \ $
–爱德华
2014年5月4日,0:38

\ $ \ begingroup \ $
由于要允许编译器进行优化,因此在计时不是函数的时间时,我会格外小心。我不确定这种程序的顺序要求和可观察到的行为理解。使用函数调用时,不允许编译器在调用点之间移动语句(它们在调用之前或之后进行排序)。
\ $ \ endgroup \ $
–马丁·约克
2014年5月6日15:52

\ $ \ begingroup \ $
+1。我觉得这很自然。我通常会按照matlab样式编写自己的计时器对象,并使用称为tic(),toc()(而不是start(),stop())的成员。加上tac(),它可以测量经过的时间而无需停止计时器。但是,所有这些都是对拟议的measure()函数模板(包括参数)的补充,而不是竞争。为什么不在计时器对象中拥有所有对象?
\ $ \ endgroup \ $
–iavr
2014年5月7日,0:53

\ $ \ begingroup \ $
@iavr:关于它们是互补机制的好点。另外,虽然我从不喜欢过分可爱的Matlab名称,但我会指出stop()并不是一个完全准确的名称,实际上其功能更像toc(),因为stop()和toc()都不会真正停止计时器。
\ $ \ endgroup \ $
–爱德华
2014年5月7日在1:51

#3 楼

您应该使用std::chrono::steady_clock而不是std::chrono::system_clock。这是因为system_clock可能会不时地进行调整(例如,它可能会倒退一点,或者比实际经过的时间向前跳更多,以便与墙壁/世界时钟保持同步),而stable_clock是单调时钟,永远不会调整。因此,steady_clock更适合您的工作。

P.S.如果您需要知道函数实际上占用了多少CPU时间,即使steady_clock也可能不是理想的选择,因为它只能测量从函数调用到返回之间的持续时间。这可能不一定等于该功能占用的CPU时间,因为其他进程和线程可能会中断您的功能并导致执行时间大大延长。如果要隔离其他系统活动的影响,则应使用Google OS /特定于平台的替代方法来获取线程执行时间或在Boost中查找chrono函数(查找process_...cpu_clockthread_clock类)。

#4 楼

代码开始接受函数参数后,没有太多改进。

这些是一些细节,您可以自由地忽略它们:



类名应以大写字母开头

struct Measure



您不需要duration变量;只需执行:

return std::chrono::duration_cast< TimeT>(std::chrono::system_clock::now() - start).count();




评论


\ $ \ begingroup \ $
我同意将类型名称以大写字母开头(这是一种常见做法)是一个好主意。但是应该有点强。
\ $ \ endgroup \ $
–马丁·约克
2014年5月3日23:25

\ $ \ begingroup \ $
不同意删除可变持续时间。编译器将删除所有浪费的空间,因此使用该变量,代码仍将是最佳的(即与您的版本没有区别)。但这有助于阅读代码(并且代码供人类阅读,因此对我们人类来说很容易,并且保留变量以使其易于阅读)。同样,在调试时,将结果存储在变量中时,可以更轻松地检查结果。
\ $ \ endgroup \ $
–马丁·约克
2014年5月3日23:27

\ $ \ begingroup \ $
我在两种观点上都不相同:出于与Loki相同的原因,删除持续时间,类名大写,因为您可以自由选择自己的代码样式(实际上标准库类不是大写的,不是吗?)
\ $ \ endgroup \ $
– pqnet
2014年8月6日下午4:58