【问题标题】:What is the fastest way to pause execution in c++在c ++中暂停执行的最快方法是什么
【发布时间】:2021-10-17 05:06:01
【问题描述】:

我想在 c++ 中暂停执行时间,这是我得到的代码:

while (_run)
{
    //pause code
    std::this_thread::sleep_for(std::chrono::nanoseconds(_interval_ns));

    //doing stuff
    _counter++;
    (*_mainSrcTick)(GetDeltaTime(), GetName());
    
    for (auto obj : _physicsList)
    {
        PyObject_CallMethod(obj, "PhysicsTick", "(d)", GetDeltaTime());
    }
    
    //used to mesure time
    LastTick = std::chrono::high_resolution_clock::now();
}

以防万一这是我的增量时间函数,它可以获取自上次滴答以来我的间隔已经过去了多少:

double difference = std::chrono::duration<double,std::nano>(std::chrono::high_resolution_clock::now() - LastTick).count();
double result = difference/_interval_ns  ;
return result;

我遇到的问题是,通过不暂停显然我的代码可以在我们几个人中运行,并且暂停一点时间甚至需要几毫秒。因此,我想要一种不会对我的程序造成太大影响的暂停方式。

在你想知道我确实知道如何使用“使用命名空间”之前,这个项目在 x64 上运行(虽然我不介意它是否也可以在 x86 上运行),将 python 与 c++ 链接,并且是多线程的,虽然在仅限蟒蛇方面。所以我认为我需要一种更快的方法来暂停或至少暂停一段时间,以便它可以通过 while 循环暂停。我不想要 ANY 第三方依赖项。我使用 VS 2019。

目的是让事情以每个_interval_ns 的固定频率发生。 GetDeltaTime 是这样的,所以我可以对 dostuff 给出任何不准确之处,但如果频率准确而不是 delta 时间会更好。我正在寻找微秒精度,nano 会很好。

【问题讨论】:

  • _interval_ns的值是多少?
  • 取决于但通常为 1 000 000
  • 如果您的 CPU 支持,请使用 felixcloutier.com/x86/umwait
  • 时间延迟的预期目的是什么?示例代码让我怀疑您想要某种next_time = get_monotomic_counter() + my_delay; while(true) { do_stuff(); delay_until_monotomic_counter(next_time); next_time += my_delay; },以便do_stuff() 以固定频率(例如每秒60 次)发生,即使do_stuff() 花费未知/可变的时间。
  • 是的,我需要“做事”以固定频率发生,并且我想要某种控制频率的方法。这个:next_time = get_monotomic_counter() + my_delay; while(true) { do_stuff(); delay_until_monotomic_counter(next_time); next_time += my_delay; } 可以很好地实现一些方法,但thread::sleep_until 太慢了。

标签: c++ performance time


【解决方案1】:

当你让你的线程休眠时,操作系统会取消它的调度,重新调度它需要一些时间。如果您想暂停一小段时间,请使用_mm_pause()。您可能希望根据自己对系统花费多长时间的测量来执行固定次数。英特尔表示在 Skylake 上需要 140 个周期(在旧 CPU 上花费的时间要少得多):https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_pause&expand=5146

如果您想睡觉的时间很短,但您关心实际已经过去了多少时间,您可以检查循环中的时间并在每个循环中调用一次_mm_pause()clock_gettime() 大约 70 ns 加上 _mm_pause() 40 ns 应该可以在 Skylake 上为您提供大约 120 ns 的分辨率。

在任何一种情况下,您都不会明确释放 CPU 内核以供其他进程使用,并且如果有其他线程等待在同一内核上运行,操作系统可能会取消调度您的线程。因此,您需要设置 CPU 亲和性,以使其他任何东西都不能在同一内核上运行。详情见https://www.suse.com/support/kb/doc/?id=000017747

另见:https://software.intel.com/content/www/us/en/develop/articles/benefitting-power-and-performance-sleep-loops.html

【讨论】:

  • 不要使用_mm_pause() - 这仅用于防止在紧密轮询循环中进行不必要的推测执行(因此当被轮询的数据发生变化时,循环可以更快地退出,并且核心中的其他逻辑处理器可以运行更快)并且与时间无关,不是时间延迟。
  • 我应该用什么在你看来不需要很长时间(相对)暂停?我目前正在尝试umwait,但如果您有更好的方法,请留下答案@Brendan。
  • 我似乎找不到太多关于如何使用 umwait 的信息,所以我仍然愿意提供答案。
  • @matapple:umwait 是一个很好的建议,除了它太新了,大多数计算机都不支持它。在循环中尝试_mm_pause(),并按照我的建议检查当前时间,看看它是否符合您的需要。如果您能告诉我们您的公差也会有所帮助(即您需要 1 微秒的精度还是什么)。
  • 它有效,我得到了我想要的微秒分辨率。
【解决方案2】:

如果您不想为线程上下文切换付费,请考虑不切换线程上下文,例如使用coroutines.Here Gor Nishanov claims that suspending and resuming a coroutine takes less than a nanosecond

【讨论】:

  • 操作系统产生一个线程,这个线程有一个上下文,例如 CPU 寄存器中的值。如果您的线程数多于 CPU,则操作系统会给每个线程一些 cpu 时间。如果一个线程被挂起然后又恢复,操作系统需要恢复这个线程的上下文。 wiki context switch
猜你喜欢
  • 2014-08-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-07-31
相关资源
最近更新 更多