C++ 中的时序逻辑错误(使用 std::chrono)

Posted

技术标签:

【中文标题】C++ 中的时序逻辑错误(使用 std::chrono)【英文标题】:Logical error in timing in C++ (using std::chrono) 【发布时间】:2019-10-03 13:46:49 【问题描述】:

我编写了一个 C++ 程序来对各种排序算法进行基准测试,以找出哪一个是最快的。但是,我在执行代码时遇到了一些问题。

我首先创建了一个类来使用构造函数和析构函数对算法进行计时。然后我使用std::chrono::time_point_cast 将时间显式转换为毫秒。但是,每次我运行我的程序时,程序最终都会显示零毫秒。

请注意,我已经包含了 chrono 头文件。

这是涉及的程序源代码部分。

类定义

int Array[20], size = 20;

class BenchmarkTimer

public:
    std::chrono::time_point<std::chrono::high_resolution_clock> startpt;
    float ms;
    long long duration;
    BenchmarkTimer() : ms(0), duration(0)
    
        startpt = std::chrono::high_resolution_clock::now();
    
    ~BenchmarkTimer()
    
        auto endpt = std::chrono::high_resolution_clock::now();
        auto init = std::chrono::time_point_cast<std::chrono::milliseconds>(startpt).time_since_epoch().count();
        auto final = std::chrono::time_point_cast<std::chrono::milliseconds>(endpt).time_since_epoch().count();
        auto duration = final - init;
    
;

选择排序功能(这只是众多排序算法中的一种)。

void SelectionSort(int Array[])

    BenchmarkTimer timer;
    int temp, smallest, position, j;
    for (int i = 0; i < size - 1; i++)
    
        smallest = Array[i];
        position = i;
        for (j = i + 1; j < size; j++)
            if (Array[j] < smallest)
            
                smallest = Array[j];
                position = j;
            
        temp = Array[i];
        Array[i] = Array[position];
        Array[position] = temp;
    
    DisplayArray(Array);
    std::cout << "\nTime taken to sort the array: " << timer.duration << " ms" << std::endl;

DisplayArray(Array) 函数调用只是将数组显示在屏幕上。

我希望程序显示经过的毫秒数。

现在,实际输出是:

Time taken to sort the array: 0 ms

但我希望输出是:

Time taken to sort the array: 13 ms

(13 毫秒只是一个例子。)

我建议你建议更简单的解决方案,因为我处于 C++ 编程的中级水平。

提前致谢!

【问题讨论】:

是的,我做到了。我什至尝试过使用nanoseconds 你在析构函数中计算了持续时间,当你调用timer.duration时析构函数还没有被调用。在一个名为 get_elapsed_time() 的函数中移动析构函数代码,该函数返回属性 duration 【参考方案1】:

你的问题是你只在BenchmarkTimer的析构函数中计算时间。这意味着duration 将始终为 0,因为它仅在析构函数中发生变化,并且在对象被析构后您无法访问该对象。

有几种方法可以解决此问题。第一种是将时序代码移到函数中。其次,您可以修改BenchmarkTimer 以在构造函数中获取一个函数对象,该对象将是要运行的代码,然后在构造函数中进行计算。看起来像

class BenchmarkTimer

public:
    std::chrono::time_point<std::chrono::high_resolution_clock> startpt;
    float ms;
    long long duration;
    template<typename Func>
    BenchmarkTimer(Func func) : ms(0), duration(0)
    
        startpt = std::chrono::high_resolution_clock::now();
        func();
        auto endpt = std::chrono::high_resolution_clock::now();
        auto diff = end-start;
        duration = diff.count();
    
;

void SelectionSort(int Array[])

    BenchmarkTimer timer([&]()
    
        int temp, smallest, position, j;
        for (int i = 0; i < size - 1; i++)
        
            smallest = Array[i];
            position = i;
            for (j = i + 1; j < size; j++)
                if (Array[j] < smallest)
                
                    smallest = Array[j];
                    position = j;
                
            temp = Array[i];
            Array[i] = Array[position];
            Array[position] = temp;
        
    );
    DisplayArray(Array);
    std::cout << "\nTime taken to sort the array: " << timer.duration << " ms" << std::endl;

另一种选择是将另一个函数添加到BenchmarkTimer,一旦你希望它进行计算,你就会调用它,并将你的析构函数代码移动到那里。请注意这一点,因为在您的析构函数中声明了一个隐藏类的durationduration 变量。代码应该是这样的

auto endpt = std::chrono::high_resolution_clock::now();
auto init = std::chrono::time_point_cast<std::chrono::milliseconds>(startpt).time_since_epoch().count();
auto final = std::chrono::time_point_cast<std::chrono::milliseconds>(endpt).time_since_epoch().count();
duration = final - init;
// ^ no auto here

【讨论】:

我希望我能采纳这个建议,但我发现它有点高级。我宁愿发现 rafix07 的建议是一个更简单但可行的替代方案。不过,感谢您的建议。 @MithunK 没问题。希望有一天你会看到这段代码,而不是认为它是如此先进。如果您好奇,我们有很多书籍here 讨论现代 C++ 和模板。它们在某些时候值得一读。 感谢您分享链接。【参考方案2】:

解决这个问题的一个非常简单的方法是将计时器的打印语句放在~BenchmarkTimer() 中。 BenchmarkTimer 构造函数可以接收一条消息来增加~BenchmarkTimer() 中的输出。这可能看起来像:

class BenchmarkTimer

public:
    std::string msg_;
    std::chrono::steady_clock::time_point startpt_;

    explicit BenchmarkTimer(std::string msg)
        : msg_(std::move(msg))
        , startpt_(std::chrono::steady_clock::now())
    
    

    ~BenchmarkTimer()
    
        using namespace std::chrono;
        auto endpt = steady_clock::now();
        std::cout << msg_ << duration_cast<milliseconds>(endpt - startpt_).count() << " ms\n";
    

    BenchmarkTimer(BenchmarkTimer const&) = delete;
    BenchmarkTimer& operator=(BenchmarkTimer const&) = delete;
;

您可以通过在要开始计时的点构造BenchmarkTimer 来使用它,并带有适当的消息,它将记录并记录计时器范围内的时间。例如:

void SelectionSort(int Array[])

    BenchmarkTimer timer"\nTime taken to sort the array: ";
    int temp, smallest, position, j;
    // ...
    DisplayArray(Array);
  // Output: Time taken to sort the array: 13 ms

【讨论】:

explicit关键字有什么用? 它可以防止 std::string 隐式转换为 BenchmarkTimer。相反,必须使用 explicit 语法,如SelectionSort 中的示例使用所示。我可能还应该修改我的答案以给 BenchmarkTimer 删除的副本成员,因为延长 BenchmarkTimer 对象的生命周期是不寻常的(并且可能是一个错误)。

以上是关于C++ 中的时序逻辑错误(使用 std::chrono)的主要内容,如果未能解决你的问题,请参考以下文章

从头学起Verilog:时序逻辑基础与回顾

复杂时序逻辑电路

Verilog使用always块实现时序逻辑

逻辑综合DC报告——时序和面积信息查看学习

数电基础:时序逻辑电路的时序分析

全面理解Gradle - 执行时序