How can I run a method every X microseconds without CPU loading (0 < X < 1000)?

I am looking for any timer's design, which can schedule tasks with the period less than 1 millisecond. The most optimal scenario: load 25% of 1 core, when 1 separate thread is used for running this pattern.

1 millisecond is the limit for all the timers, which I know:

I have already implemented timer, which runs task every X microseconds. But it is still loading almost one core, when 1 separate thread is used for running this pattern.

The code:

private readonly TimeSpan _period;

private readonly Stopwatch _stopwatch;

private TimeSpan _lastElapsedTime;

private Double _iterationsPerPeriod;

void Run() 
{
    TimeSpan elapsed = _stopwatch.Elapsed;
    TimeSpan interval = elapsed - _lastElapsedTime;
    if (interval < _period)
    {
        Int32 iterationsToWait = (Int32) (_iterationsPerPeriod * (interval.TotalMilliseconds / _period.TotalMilliseconds));
        Thread.SpinWait(iterationsToWait);
    }

    _lastElapsedTime = _stopwatch.Elapsed;
    // RunTask();
}

void RunTimer() 
{
    while(true)
    {
        Run();
    }
}

How can I run a method every X microseconds without CPU loading?

Upvotes: 2

Views: 609

Answers (1)

Luaan
Luaan

Reputation: 63732

The only way to "stop" a CPU is to halt it - and that's a kernel-level instruction on Windows. The only way to wake a CPU is to give it an interrupt - again, kernel-level. While it's possible to create a timer interrupt with far higher accuracy than milliseconds on x86, you'd need to make your own OS (possibly virtualized - if it uses the CPU exclusively) to use it.

On application-level, you need to rely on the available interfaces entirely. And you need to coöperate with other processes running on the same CPU/OS. This means you need to use the OS task scheduling - and Windows task scheduling has limits. The shortest interval you could possibly wait for depends on what the OS allows - and on Windows, this used to be related to the system timer (~20ms, configurable down to 0.5ms). But even with the newer schedulers, you can't just arbitrarily schedule a thread to run with microsecond accuracy.

If you need such hard-realtime environments, you simply can't use Windows, or any other preëmptively multi-tasked system, really. Sleeping every other half-a-millisecond is already pushing the realistic capabilities of modern systems running hundreds of processes with thousands of threads at the same time. Busy-waiting can get you close (and indeed is still used in games) if you can afford that thread to pretty much claim the CPU for itself - otherwise, other threads can easily steal the CPU from your thread (easily 20ms or more). Of course, it will still quickly fail if the system gets under significant load.

As always, it's a game of trade-offs. Making a modern multi-tasked system that could schedule threads with such accuracy would be possible, but isn't really worth it. Even in old games with exclusive CPU access, you usually wouldn't "sleep for 20us" - instead, you'd wait for a vertical retrace interrupt or something like that (and this is still somewhat available in DirectX/OpenGL/Vulkan...). There's very few applications that would require both a modern multi-tasked system, and a scheduler accuracy that high. In most user applications, you can use an algorithm that allows you to sleep 25% of the time without switching thread contexts every 20us (e.g. doing more work every "frame", and sleeping accordingly longer).

Upvotes: 3

Related Questions