Quasi real time con windows
Nozioni per programmare applicazioni "quasi real time" in ambiente Windows.
Scritto nel periodo in cui lavoriamo con Windows 10 IoT LTSC
Tutto questo vale se si lavora con tempi molto ristetti, per esempio un algoritmo deve compiere tutta la sua elaborazione in 50 ms e ritardi dell'ordine dei 5-10 ms iniziano ad essere significativi.
1) Windows non misura bene il tempo, ma si parla di 1-2 ms. Questo vale anche per gli algoritmi di Halcon o altro che usano un timeout. Ovviamente anche lo stopwatch del C#.
2) Un comando di pausa/sleep o simili in un ciclo ha effetti devastanti: una sleep di 1 ms potrebbe aspettare 10 ms o oltre. Questo è stato testato non solo con un programma C# ma anche con HDevelop e Sherlock. Una sleep in un ciclo abbassa la resa del task/thread in cui gira.
L'unica alternativa per andare veloci è un ciclo senza sleep che va al massimo, userà tutta la CPU ma gira molto basso, sul vecchio PC laboratorio, testato con l'oscilloscopio andava a 130 microsecondi. Facendo riferimento al punto 1, meglio avere 1-2 ms di incertezza leggendo uno stopwatch in un ciclo whicle 'attivo' ossia senza sleep.
3) Se in task (C#) va in eccezione può rallentare tutti gli altri task. Sembra assurdo ma lo abbiamo replicato con un semplice programma C#. Dai 2-3 ms in su.
4) Al task sarebbe meglio dare l'opzione "longRunning" così ogni task ha il suo thread e non sono tutti in un pool di thread:
var task3 = new Task(() => MyLongRunningMethod(), TaskCreationOptions.LongRunning | TaskCreationOptions.PreferFairness);
task3.Start();
5) Il compilatore, JIT o il processore possono riordinare le operazioni di memoria per ottimizzare la performance.
Questo è negativo quando per esempio ci serve assegnare in una determinata sequenza delle variabili.
Per evitare questo problema si possono usare le memory barrier. Ogni assegnazione che si trova prima del memory barrier non verrà effettuata dopo di esso, però l'ordine in cui vengono eseguite le assegnazioni prima del memory barrier potrebbe non essere rispettato.
x = 1; y = 2; z = 3; Thread.MemoryBarrier(); k = 4;
In questo esempio le assegnazioni possono avvenire in questo ordine:
x ➡ y ➡ z ➡ k
x ➡ z ➡ y ➡ k
y ➡ x ➡ z ➡ k
y ➡ z ➡ x ➡ k
z ➡ x ➡ y ➡ k
z ➡ y ➡ x ➡ k
k non verrà mai assegnata prima di x, y e z, ma l'ordine in cui x, y e z vengono assegnate varia.