【问题标题】:Nanosecond-level C++ program idling / throttling纳秒级 C++ 程序空闲/节流
【发布时间】:2020-09-03 10:59:23
【问题描述】:

我正在编写一个运行微控制器仿真器的 C++ 程序。运行模拟器的线程在这样的循环中执行此操作:

while(should_run) {
  simulator->RunSingleClockCycle();
}

在我的笔记本电脑上,它能够以大约 60 MHz 的频率运行模拟器。由于我只需要模拟器以 16MHz 运行,因此我可以在此 while 循环中使用 busyloop 来减慢它的速度,使其以 16MHz 运行,因为不可能再休眠一纳秒。

由于该线程正在运行模拟器并忙于循环,它会导致我的一个内核上的 CPU 使用率为 100%。如果有办法以某种方式限制线程,使其以 16MHz 的频率运行微控制器(没有繁忙循环),它将减少仿真器的资源使用。

是否有任何半跨平台(macos + linux)的方式可以在 C++ 中实现这种线程节流?

【问题讨论】:

  • 注意:在传统的 PC 硬件上不可能获得纳秒计时。你很幸运能比毫秒计时好得多,因为它们不是为此而构建的。繁忙的循环可能是您能做的最好的事情。

标签: c++ multithreading emulation


【解决方案1】:

我认为您可能正在寻找类似的东西

std::this_thread::sleep_for(std::chrono::nanoseconds(1));

另见:

https://en.cppreference.com/w/cpp/thread/sleep_for

https://en.cppreference.com/w/cpp/thread/sleep_until

https://en.cppreference.com/w/cpp/chrono/duration

【讨论】:

  • 实际上,操作系统实际上不会在这里休眠一纳秒,而是一个可能几十毫秒的时间片。 slee_for(x) 的保证是它会休眠“至少”x
  • @Conor 我知道。但它尽可能接近std:: wise。
【解决方案2】:

在传统硬件上,您永远无法获得纳秒级计时。例如,在我的系统上运行此代码(不考虑竞争条件):

#include <thread>
#include <chrono>
#include <future>
#include <iostream>



int main()
{
    unsigned int counter = 0;
    auto res = std::async(std::launch::async, [&]()
    {
        while (true)
        {
            std::cout << "Count : " << counter << '\n';
            counter = 0;
            std::this_thread::sleep_for(std::chrono::seconds(1));
        }
    });


    auto timetoWait = 1'000'000'000 / 16'000'000;
    while (true)
    {
        ++counter;
        std::this_thread::sleep_for(std::chrono::nanoseconds(timetoWait));
    }
}

我每秒最多可以进行 600-700k 次“操作”。与模拟器应该运行的 1600 万相比相差甚远。

你最好只忙循环。

【讨论】:

    【解决方案3】:

    您可以为此目的使用 chrono,我很确定它适用于 linux 和 windows。

    我不知道有什么方法可以 100% 准确地测量时间。这些计时码表本身会调用一些时间,但它们相对准确。

    #include <iostream>
    #include <chrono>
    
    bool wait(long long nanoseconds)
    {
        // if the number of ns to wait is not zero
        if(0 != nanoseconds)
        {
            // init high resolution clock
            std::chrono::high_resolution_clock                              hrc             = {};
            // get the start and stop timepoints
            std::chrono::time_point<std::chrono::high_resolution_clock>     start           = hrc.now(),
                                                                            stop            = hrc.now();
            // calculate the number of ns passed by subtracting the time at the start from the time at the stop
            std::chrono::nanoseconds                                        time_passed     = stop - start;
    
            // while the number of ns to wait for is bigger than the number of ns passed
            while(nanoseconds > time_passed.count())
            {
                // get the new stop timepoint
                stop            = hrc.now();
                // calculate the new number of ns passed
                time_passed     = stop - start;
            }
    
            // the wait has ended
            return true;
        }
    
        // the function failed
        return false;
    }
    
    int main()
    {
        printf("start\n");
        // wait for 5 seconds
        wait(5000000000);
        printf("stop\n");
        getchar();
    
        return 0;
    }
    

    您也可以使用 rdtsc 指令以获得更高的准确性,但我无法使示例代码正常工作,所以我还是将其发布。

    这是汇编代码(我的 IDE,Visual Studio,不支持 x64 上的内联汇编,所以我不得不单独编写)

    .model flat, c
    
    .code
    
    get_curr_cycle proc
    cpuid
    cpuid
    cpuid
    rdtsc
    shl edx, 32
    or edx, eax
    mov eax, edx
    retn
    get_curr_cycle endp
    
    end
    

    这是 c++ 代码。

    #include <iostream>
    
    extern "C" unsigned int get_curr_cycle();
    
    bool wait(long long nanoseconds)
    {
        if(0 != nanoseconds)
        {
            unsigned int    start           = get_curr_cycle(),
                            stop            = get_curr_cycle();
            unsigned int    time_passed     = (stop - start);
    
            while(nanoseconds > time_passed)
            {
                stop            = get_curr_cycle();
                time_passed     = (stop - start);
            }
        }
    
        return false;
    }
    
    int main()
    {
        printf("start\n");
        // wait for 5 seconds
        wait(5000000000);
        printf("stop\n");
        getchar();
    
        return 0;
    }
    

    【讨论】:

    • while 仍然会导致 100% 的 CPU 负载,这是 OP 希望避免的。
    • 我想我对 OPs 问题有点困惑。 OP 可以使用 SetThreadAffinity 来帮助更顺利地运行吗?
    猜你喜欢
    • 2020-09-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多