bas
bas

Reputation: 14902

Limits of (soft real-time) timing requirements in Windows OS

In the company I work for we build machines which are controlled by software running on Windows OS. A C# application communicates with a bus controller (via a DLL). The bus controller runs on a tact time of 15ms. That means, that we get updates of the actual sensors in the system with a heart beat of 15ms from the bus controller (which is real time).

Now, the machines are evolving into a next generation, where we get a new bus controller which runs on a tact of 1ms. Since everybody realizes that Windows is not a real time OS, the question arises: should we move the controlling part of the software to a real time application (on a real time OS, e.g. a (soft) PLC).

If we stay on the windows platform, we do not have guaranteed responsiveness. That on itself is not necessarily a problem; if we miss a few bus cycles (have a few hickups), the machine will just produce slightly slower (which is acceptable).

The part that worries me, is Thread synchronization between the main machine controlling thread, and the updates we receive from the real time controller (every millisecond).

Where can I learn more about how Windows / .NET C# behaves when it goes down the path of thread synchronization on milliseconds? I know that e.g. Thread.Sleep(1) can take up to 15 ms because Windows is preempting other tasks, so how does this reflect when I synchronize between two threads with Monitor.PulseAll every ms? Can I expect the same unpredictable behavior? Is it asking for trouble when I am moving into the soft real time requirements of 1ms in Windows applications?

I hope somebody with experience on these aspects of threading can shed some light on this. If I need to clarify more, by all means, shoot.

Upvotes: 4

Views: 1493

Answers (2)

Raheel Khan
Raheel Khan

Reputation: 14777

Your scenario sounds like a candidate for a kiosk-mode/dedicated application.

In the company I work for we build machines which are controlled by software running on Windows OS.

If so, you could rig the machines such that your low-latency I/O thread could run on a dedicated core with thread and process priorities maximized. Furthermore, ensure the machine has enough cores to handle a buffering thread as well as any others that process your data in transit. The buffer should allocate memory upfront if possible to avoid garbage collection bottlenecks.

@Aron's example is good for situations where data integrity can be compromised to a certain extent. In audio, latency matters a lot during recording for multiple reasons but for pure playback, data loss is acceptable to a certain degree. I am assuming this is not an option in your case.

Of course Windows is not designed to be a real-time OS but if you are using it for a dedicated app, you have control over every aspect of it and can turn off all unrelated services and background processes.

I have had a reasonable amount of success writing software to monitor how well UPS units cope with power fluctuations by measuring their power compensation response times (disclaimer: not for commercial purposes though). Since the data to measure per sample was very small, the GC was not problematic and we cycled pre-allocated memory blocks for buffers.

Some micro-optimizations that came in handy:

  • Using immutable structs to poll I/O data.
  • Optimizing data structures to work well with memory allocation.
  • Optimizing processing algorithms to minimize CPU cache misses.
  • Using an optimized buffer class to hold data in transit.
  • Using the Monitor and Interlocked classes for synchronization.
  • Using unsafe code with (void*) to gain easy access to buffer arrays in various ways to decrease processing time. Minimal use of Marshal and Buffer.BlockCopy.

Lastly, you could go the DDK way and write a small driver. Albeit off-topic, DFMirage is a good example of a video driver that provides both an event-based and a polling model for differential screen capture such that the consumer application can chose on-the-fly based on system load.

As for Thread.Sleep, you could use it as sparingly as possible considering your energy consumption boundaries. With redundant processes out of the way, Thread.Sleep(1) should not be as bad as you think. Try the following to see what you get. Note that this has been coded in the SO editor so I may have made mistakes.

Thread.CurrentThread.Priority = ThreadPriority.Highest;
Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.RealTime;

var ticks = 0L;
var iteration = 0D;
var timer = new Stopwatch();

do
{
    iteration++;
    timer.Restart();
    Thread.Sleep(1);
    timer.Stop();
    ticks += timer.Elapsed.Ticks;

    if (Console.KeyAvailable) { if (Console.ReadKey(true).Key == ConsoleKey.Escape) { break; } }

    Console.WriteLine("Elapsed (ms): Last Iteration = {0:N2}, Average = {1:N2}.", timer.Elapsed.TotalMilliseconds, TimeSpan.FromTicks((long) (ticks / iteration)).TotalMilliseconds);
}
while (true);

Console.WriteLine();
Console.WriteLine();
Console.Write("Press any key to continue...");
Console.ReadKey(true);

Upvotes: 1

Aron
Aron

Reputation: 15772

Come to think about the actual problem itself, processing data at 1ms is pretty easy. When considering audio recording, as an analogous (pun not intended) problem, you might be able to find some inspiration in how to achieve your goals.

Bear in mind.

  • Even a modest setup can achieve 44.1kHz@16bit per channel sampling rate (that is about 22microseconds or less than a hundredth of your target).
  • Using ASIO you can achieve sub 10ms latencies
  • Most methods of achieving high sampling rates will work by increasing your buffer size and sending data to your system in batches
  • To achieve the best throughput, don't use threads. You DMA and interrupts to callback your processing loop.

Given that sound cards routinely can achieve your goals, you might have a chance.

Upvotes: 0

Related Questions