Una guida pratica per avvicinarsi il più possibile a Soft Real-Time su Windows ordinario

· Aggiornato il: · · Sviluppo Windows, Soft real-time, Progettazione, Misurazione

Quando si crea l’elaborazione su Windows dove “essere in ritardo è un problema” - elaborazione periodica, audio, video, misurazione, controllo dell’attrezzatura - tende ad emergere l’impressione che “Windows non può davvero farlo”. Questa impressione è per metà giusta e per metà sbagliata: Windows non è un hard real-time OS, ma se definisci correttamente progettazione, implementazione, misurazione e operazioni, puoi portarlo a uno stato genuinamente pratico come soft real-time.

Ciò che tratta in questo articolo è Windows 10 / 11 ordinario, senza estensioni speciali RTOS, driver del kernel personalizzati o controller dedicati. Si tratta di una discussione orientata alla pratica su quanto è possibile ridurre la latenza e il jitter con un’app in modalità utente su un PC desktop o portatile per l’uso quotidiano. Audio, video, controllo periodico e acquisizione dati differiscono nei dettagli, ma i punti problematici sono ampiamente condivisi, quindi questa volta abbiamo raccolto questo terreno comune sotto forma di una lista di controllo.

Sommario

  1. Prima la conclusione (in una riga)
    • 1.1. Tabella rapida per intervallo periodico
  2. Cosa significa “Soft Real-Time” su Windows ordinario
    • 2.1. Cosa significa questo articolo per “Windows ordinario”
    • 2.2. Cosa è realizzabile e dove diventa difficile
    • 2.3. Una breve parola sulla terminologia
  3. Le principali cause di latenza e jitter
    • 3.1. Lo scheduler e le priorità
    • 3.2. DPC / ISR e driver
    • 3.3. Errori di pagina e memoria
    • 3.4. Risoluzione del timer e gestione dell’alimentazione
    • 3.5. Migrazione del nucleo e calore
  4. Una lista di controllo pratica per ridurre i ritardi sul Windows ordinario
    • 4.1. Cicli periodici e come aspettare
    • 4.2. Percorso Veloce / Percorso Lento e Code a Lunghezza Fissa
    • 4.3. Priorità / MMCSS / Modalità Background
    • 4.4. Memoria / GC / Costi di prima esecuzione
    • 4.5. Impostazioni di alimentazione / EcoQoS / Risoluzione timer
    • 4.6. Posizionamento CPU / Migrazione core / Riscaldamento
    • 4.7 Isolamento dei driver / DPC / ISR / Disturbi esterni
  5. Misurazione e valutazione
    • 5.1. Cosa registrare
    • 5.2. Come leggere p99 / p99.9 / max
    • 5.3. Con cosa misurare
    • 5.4. Disciplina di prova
  6. Criteri pratici per decidere
    • 6.1. Quando giudicare “con Windows ordinario non basta”
    • 6.2. Quando far coesistere GUI / logging / comunicazione / DB
  7. Conclusione
  8. Riferimenti

1. Prima la conclusione (in una riga)

  • Ciò che miri a Windows ordinario non è una garanzia hard real-time, ma una configurazione soft real-time che “è improbabile che arrivi in ritardo e non si rompa quando lo è.”
  • La vittoria più grande è stata rendere il percorso caldo breve, di lunghezza fissa e non bloccante.
  • Separare il percorso veloce (acquisizione / controllo) dal percorso lento (storage / comunicazione / UI) e collegarli con una coda a lunghezza fissa.
  • Guidare il ciclo periodico su scadenze assolute, non appoggiandosi a Sleep(1).
  • Per flussi continui come audio e video, considera prima MMCSS.
  • Per la misurazione del tempo, utilizzare QueryPerformanceCounter (QPC) - in .NET, Stopwatch.
  • Per l’attesa, preferisci eventi dispositivo o timer di attesa ad alta risoluzione.
  • Utilizzare timeBeginPeriod solo per il tempo necessario. Non progettare partendo dal presupposto che sia sempre attivo.
  • Nel funzionamento reale, l’alimentazione CA / la modalità di alimentazione / come viene gestito l’EcoQoS / la riduzione del carico causato dalle attività in background valgono lo sforzo.
  • Valuta non solo le medie, ma p99 (la soglia in cui inizia a mostrare il valore 1 su 100 più lento) / p99.9 / max / conteggio errori / DPC / ISR / errori di pagina / profondità della coda.

In breve, nel Windows ordinario, ridurre le ragioni del ritardo attraverso la progettazione batte l’innalzamento delle priorità. Le priorità e le impostazioni di alimentazione contano, ma da sole non possono creare stabilità.

1.1. Tabella rapida per intervallo periodico

Questo è un articolo lungo, quindi inseriamo una tabella rapida all’inizio per poter leggere solo le righe pertinenti al proprio caso. Prima era in fondo (sezione 6); l’abbiamo anticipata qui.

Intervallo / Requisito Configurazione di prima battuta Sezione da leggere
10-20 ms, con assorbimento di oscillazioni occasionali Separazione fast path / slow path, code a lunghezza fissa, priorità normale-leggermente alta, progettazione guidata dagli eventi. Spesso basta 4.1, 4.2
1-5 ms, da sostenere continuamente In aggiunta a quanto sopra: percorso caldo senza allocazioni, thread dedicati, MMCSS o attenta regolazione delle priorità, timer di attesa ad alta risoluzione, alimentazione CA e revisione delle impostazioni energetiche 4.1 - 4.5
Meno di 1 ms, da rispettare anche a lungo e sotto carico Molto difficile con la sola modalità utente su Windows ordinario. Progetta prima di spostare la parte critica altrove (firmware del dispositivo, controller dedicato, FPGA, RTOS) 2.2, 6
GUI / logging / comunicazione / DB tutti insieme Non stipare tutto in “un processo, un ciclo”: separa le responsabilità. Le problematiche a valle superano facilmente le scadenze a monte 4.2, 4.3, 6

La diagnosi delle cause e la disciplina di prova sono comuni a ogni intervallo (capitoli 3 e 5).

2. Cosa significa “Soft Real-Time” su Windows ordinario

2.1. Cosa significa questo articolo per “Windows ordinario”

Per Windows ordinario assumiamo approssimativamente quanto segue.

  • Un tipico PC desktop o portatile Windows 10 / 11
  • Nessuna estensione RTOS personalizzata
  • Nessuno sviluppo di driver personalizzati in modalità kernel
  • Una normale app in modalità utente
  • Sintonizzazione con Windows API standard e impostazioni

In altre parole, non si tratta non di costruire un’intera macchina dedicata per il controllo in tempo reale: si tratta di quanto lontano puoi realisticamente spingere le cose su un normale PC Windows.

PC Windows 10 / 11 normaleApp in modalità utentePunta a soft real-timeMantieni bassa la latenzaMantieni il jitter bassoRispettare le scadenze mancate ed evitare rottureNecessità di garantire zero scadenze non rispettateRTOS / controller dedicato / FPGA / elaborazione lato dispositivo

2.2. Cosa è realizzabile e dove diventa difficile

Anche in Windows ordinario, puoi creare una configurazione veramente “raramente tardiva” per l’elaborazione come la seguente.

  • Elaborazione periodica da pochi millisecondi a decine di millisecondi
  • Audio / video gestito da buffer
  • Acquisizione sensori e anelli di controllo
  • Elaborazione a periodo fisso in stile SoftPLC
  • Una pipeline a bassa latenza in esecuzione su un thread separato dall’UI

Detto questo, “realizzabile” in questo caso non significa il picco occasionale di latenza può essere ridotto allo zero assoluto. Lo stato a cui miriamo è questo:

  • Mantieni bassa la latenza nel caso normale
  • Mantieni il jitter basso
  • Non interrompere quando occasionalmente una scadenza viene mancata
  • Essere in grado di osservare il fatto che è mancato

Al contrario, requisiti come i seguenti diventano molto difficili da soddisfare con la sola modalità utente su Windows ordinario.

  • Garantire zero scadenze mancate
  • Mantenere stabilmente al di sotto di alcune centinaia di microsecondi per lunghi periodi
  • Coesistere con una GUI pesante, una rete pesante e un storage pesante
  • Farlo con l’alimentazione a batteria o con le priorità di risparmio energetico intatte
  • Non tollerare nemmeno picchi causati da driver o dispositivi

Per questi, è più sicuro considerare anche di spostare solo la parte veramente critica in termini di tempo nel firmware lato dispositivo, in un controller dedicato, in un FPGA o in un RTOS.

2.3. Una breve parola sulla terminologia

Innanzitutto definiamo i termini utilizzati in questo articolo.

Termine In una riga Visione pratica
Soft real-time Possono verificarsi ritardi occasionali; l’approccio è renderlo piccolo e sostenibile Questo è l’obiettivo a cui puntare innanzitutto su Windows ordinario
Hard real-time Un mondo in cui bisogna garantire zero scadenze mancate Non è un obiettivo per la sola modalità utente su Windows ordinario
Jitter Variazione del periodo o del tempo di risposta Anche con una buona media, un jitter elevato significa instabilità nel funzionamento reale
Scadenza mancata L’elaborazione non termina entro l’orario previsto Non nasconderlo: contalo e registralo
p99 / p99.9 Metriche per osservare la coda lenta p99 è “la soglia in cui inizia a comparire l’1 su 100 più lento”
DPC / ISR Elaborazione lato kernel attorno ai driver e interrupts Quando è lungo, i thread in modalità utente vengono fatti attendere
MMCSS Il meccanismo Windows che alloca la CPU per lavori urgenti come audio / video Una forte opzione per l’elaborazione che non deve mai affamare i propri buffer
QPC QueryPerformanceCounter La base per la misurazione del tempo trascorso: un contatore ad alta risoluzione, non l’orologio da parete

3. Le principali cause di latenza e jitter

I motivi per cui l’elaborazione periodica resta indietro rispetto a Windows ordinario sono quasi sempre riconducibili a una delle caselle di questo diagramma.

L'elaborazione periodica resta indietroPianificatore / prioritàDPC / ISR / driverErrori di pagina / memoriaRisoluzione timer / gestione alimentazioneMigrazione / calore del nucleo

3.1. Lo scheduler e le priorità

I thread Windows vengono eseguiti in ordine di priorità. A pari priorità si alternano in round robin e quando un thread con priorità più alta diventa eseguibile, i thread con priorità più bassa vengono messi da parte.

Quindi, anche se scrivi diligentemente il tuo thread periodico, è del tutto normale che quanto segue venga eseguito per primo:

  • Altri thread
  • Altri processi
  • lavoro interno dell’OS
  • Prodotti per la sicurezza
  • Elaborazione dell’assistente del dispositivo
  • Sincronizzazione in background

3.2. DPC / ISR e driver

Questa parte è piuttosto importante. Anche con le priorità lato app in ordine, se DPC (chiamate di procedura differite) o ISR (Interrupt routine di servizio) durano a lungo, i thread in modalità utente non possono essere eseguiti durante quel periodo.

I dispositivi e i driver che comunemente causano questo problema includono:

  • USB
  • Wi-Fi / Bluetooth
  • Storage
  • Audio
  • GPU
  • ACPI / gestione dell’alimentazione

Anche quando il codice dell’applicazione va bene, puoi rimanere bloccato a causa di circostanze del driver o dell’hardware. Pensare “Aumenterò semplicemente la priorità della mia app e vincerò” qui di solito finisce in dolore.

3.3. Errori di pagina e memoria

Se si verifica un errore di pagina (una pagina necessaria che non è in memoria e deve essere recuperata) sull’hot path, la latenza aumenta immediatamente.

Modelli particolarmente da evitare:

  • Commit della pagina al primo accesso
  • Caricamento lento
  • Paginazione dei file mappati in memoria
  • Allocazione più dinamica del necessario
  • Oggetti di grandi dimensioni o heap frammentato

Per il corpo dell’elaborazione periodica, la postura corretta è più o meno: allocare la memoria necessaria in anticipo e toccarla una volta all’avvio.

3.4. Risoluzione del timer e gestione dell’alimentazione

“Voglio eseguire ogni 1 ms, quindi Sleep(1)” non funziona quasi mai. La precisione dell’attesa Windows è influenzata dalla risoluzione del timer, dalla pianificazione e dallo stato di alimentazione.

Inoltre, non trascurare che l’aumento della risoluzione del timer migliora leggermente la precisione dell’attesa ma ha effetti collaterali sul consumo energetico e sul comportamento generale del sistema.

3.5. Migrazione del nucleo e calore

Quando un thread migra tra i core, le cache devono riscaldarsi nuovamente. L’OS spesso se la cava bene da solo, ma sotto carico pesante diventa fonte di oscillazioni.

Anche il calore diventa non trascurabile sui lunghi periodi. Quando entra in azione la limitazione termica, un periodo precedentemente stabile può andare in pezzi.

4. Una lista di controllo pratica per ridurre i ritardi nel Windows ordinario

Qui inizia la parte pratica. Per le cause viste nella sezione precedente, riassumiamo cosa controllare, cosa evitare e cosa decidere prima sul Windows ordinario, sotto forma di checklist.

4.1. Cicli periodici e come aspettare

Innanzitutto il classico anti-pattern è questo.

while (running)
{
    Sleep(1);
    Step();
}

Questo non è un “periodo di 1 ms”: è un ciclo che attende circa 1 ms o più, quindi aggiunge in cima il tempo di esecuzione di Step(). Quel che è peggio, il superamento dell’attesa si accumula direttamente.

Basato sulla scadenza assolutaBasato sul tempo relativoWaitUntil(next - margin)next += periodSe necessario, centrifuga breveFastStep()Step()Sleep(1)L'errore di attesa e il tempo di esecuzione si accumulano poco a pocoResiste all'accumulo di deriva

Lista di controllo

  • Il ciclo periodico non è costruito su Sleep(1)
  • Il periodo è guidato da scadenze assolute tramite next += period
  • L’attesa preferisce eventi dispositivo o timer di attesa
  • Solo la regolazione fine finale utilizza un giro occupato molto breve
  • timeBeginPeriod viene utilizzato solo quando necessario e ripristinato in seguito
  • Il comportamento è stato verificato mentre era ridotto a icona / nascosto / non visibile

Un ciclo periodico è più stabile se guidato da scadenze assolute piuttosto che da tempo relativo.

int64_t next = QpcNow() + periodTicks;

while (running)
{
    WaitUntil(next - wakeMarginTicks);

    while (QpcNow() < next)
    {
        CpuRelax(); // Spin briefly only at the very end
    }

    int64_t started = QpcNow();
    FastStep();
    int64_t finished = QpcNow();

    RecordTiming(next, started, finished);

    next += periodTicks;

    while (finished > next)
    {
        ++missedDeadlines;
        next += periodTicks;
    }
}

4.2. Percorso Veloce / Percorso Lento e Code a Lunghezza Fissa

La base dell’architettura è mettere solo il lavoro sensibile alle scadenze sul percorso veloce e spingere tutto il resto sul percorso lento.

Eventi del dispositivo / acquisizionepercorso veloce: acquisizione, controllo, copia minimaCoda a lunghezza fissapercorso lento: archiviazione, invio, UI, aggregazioneRegistra ritardi / manchi / profondità della coda

Limitare il percorso veloce a più o meno questo valore.

  • Acquisizione dati
  • Calcolo del valore di controllo
  • La copia minima necessaria
  • Timestamp
  • Accodamento
  • Registrazione di mancati / superamenti

Tutto il resto cade sul percorso lento.

Lista di controllo

  • Nessun file scrive, invia in rete o scrive DB sul percorso attivo
  • Nessuna registrazione pesante, Flush o RPC sincrono sul percorso attivo
  • Percorso veloce / percorso lento chiaramente separati da filo conduttore o responsabilità
  • La coda è a lunghezza fissa
  • La politica per l’overflow della coda viene decisa in anticipo
  • Vengono osservati i conteggi mancati, i conteggi eliminati e la profondità della coda
  • Gli aggiornamenti dell’UI e l’aggregazione dei log vengono separati con una frequenza inferiore

Quando la coda si riempie, è più sicuro non lasciare la policy nel vago.

Latest value mattersEvery record mattersLogging useLa coda è pienaCosa proteggiamo?Elimina le vecchie voci e mantieni le ultimeAvviso / arresto / limitazione a monteElimina le vecchie voci, registra solo il conteggio delle cadute

4.3. Priorità / MMCSS / Modalità background

La regola base delle priorità è: non rilanciare tutto. In Windows ordinario, “sollevare solo i thread importanti e ridurre adeguatamente il lavoro di back-office” funziona meglio. La modalità in background è un meccanismo che tratta non solo la CPU ma anche le risorse come I / O con priorità inferiore.

Dividi il lavoroThread sensibili alla scadenzaMemorizza / invia / comprimi / aggregaUIPriorità più alta o MMCSS se necessarioModalità background / priorità inferiorePriorità normaleNon iniziare con REALTIME_PRIORITY_CLASS

Lista di controllo

  • Non tutti i thread hanno priorità alta
  • Vengono sollevati solo i thread veramente critici in termini di tempo
  • Le attività di back-office come l’archiviazione, l’invio, la compressione e la sincronizzazione vengono spostate in modalità background
  • MMCSS viene considerato per l’elaborazione continua del buffer come audio, video, acquisizione e riproduzione
  • Pensare prima al thread, prima all’intero processo
  • REALTIME_PRIORITY_CLASS non viene utilizzato finché la necessità non è chiaramente stabilita

MMCSS (Multimedia Class Scheduler Service) è particolarmente efficace per l’elaborazione che deve “riempire un buffer entro un tempo fisso”, come audio / video. Si allinea al design di Windows meglio che semplicemente girare sempre un thread ad alta priorità.

Il codice assomiglia più o meno a questo.

DWORD taskIndex = 0;
HANDLE avrt = AvSetMmThreadCharacteristicsW(L"Pro Audio", &taskIndex);
if (!avrt)
{
    throw std::runtime_error("AvSetMmThreadCharacteristicsW failed");
}

// Run the time-sensitive loop

if (!AvRevertMmThreadCharacteristics(avrt))
{
    throw std::runtime_error("AvRevertMmThreadCharacteristics failed");
}

4.4. Memoria / GC / Costi di prima esecuzione

Se utilizzi new / malloc / List<T>.Add / concatenazione di stringhe / LINQ a ogni passaggio attraverso il percorso attivo, prima o poi emergeranno i costi di raccolta e riposizionamento. GC (garbage collection) in sé non è il cattivo, ma se scrivi codice con un’allocazione pesante, il suo impatto emergerà come jitter.

AvvioAssegnare i buffer necessariToccateli una volta per riscaldare le pagineTogli di mezzo il caricamento JIT / DLL / il primo I / OQuindi eseguire la misurazione / operazione reale

Lista di controllo

  • Nessuna allocazione / deallocazione della memoria per iterazione sul percorso attivo
  • I buffer richiesti vengono preallocati all’avvio
  • Le pagine vengono riscaldate toccandole una volta all’avvio
  • Il primo JIT, il primo DLL carico e il primo I / O non vengono mescolati nella misurazione reale
  • Nessuna struttura enorme o tronchi di lunghezza variabile che crescono all’interno del circuito
  • Se VirtualLock viene utilizzato, è limitato a una regione critica molto piccola

.NETcontrolli lato

  • La misurazione del tempo utilizza Stopwatch / Stopwatch.GetTimestamp()
  • Nessuna LINQ, concatenazione di stringhe, ToString() o generazione di log di grandi dimensioni sul percorso attivo
  • async / await non viene portato nel percorso caldo
  • Il preriscaldamento e il postriscaldamento vengono valutati separatamente

4.5. Impostazioni di alimentazione / EcoQoS / Risoluzione timer

Questa parte è poco affascinante ma efficace. Per quanto rigoroso sia il tuo codice, i risultati non si stabilizzeranno se il controllo di potenza di livello superiore agisce con forza.

Gestione energetica su Windows ordinarioFunziona con alimentazione CAModalità di alimentazione: tendente alle migliori prestazioniUn piano energetico di produzione dedicato, se necessarioTieni i processi urgenti lontano da EcoQoSControlla come vengono gestite le richieste di risoluzione del timer

Lista di controllo

  • La valutazione della produzione viene eseguita prima sull’alimentazione CA
  • Settings > System > Power & battery > Power mode è impostato su Prestazioni migliori
  • Le modalità di risparmio batteria / risparmio energetico non sono attive durante le corse
  • Sono state controllate le modalità silenziosa / eco / batteria-prima delle utenze specifiche del fornitore
  • I processi urgenti non vengono collocati incautamente nel EcoQoS (orientamento all’efficienza energetica QoS)
  • IGNORE_TIMER_RESOLUTION non è abilitato nel processo sensibile al fattore tempo
  • Verificato se le richieste di risoluzione del timer perdono effetto quando ridotte a icona / nascoste
  • Le impostazioni di potenza per l’uso quotidiano e per produzione / misurazione / demo vengono mantenute separate

timeBeginPeriod è utile se utilizzato in modo organizzato, ma non è una panacea.

  • Chiamalo appena prima che sia necessario
  • Al termine, ripristina con timeEndPeriod
  • Dalla versione Windows 10 2004 in poi, non ha più il comportamento completamente globale del passato
  • Su Windows 11, un processo con finestre completamente nascoste / minimizzate / non visibili / non udibili potrebbe non essere garantita ad alta risoluzione
  • Aumentando la risoluzione non aumenta la precisione di QPC

Se si sospettano effetti di potenza o QoS, controllare lo stato di limitazione della potenza con SetProcessInformation.

PROCESS_POWER_THROTTLING_STATE state{};
state.Version = PROCESS_POWER_THROTTLING_CURRENT_VERSION;
state.ControlMask =
    PROCESS_POWER_THROTTLING_EXECUTION_SPEED |
    PROCESS_POWER_THROTTLING_IGNORE_TIMER_RESOLUTION;
state.StateMask = 0; // HighQoS (performance-leaning) + honor timer-resolution requests

if (!SetProcessInformation(
        GetCurrentProcess(),
        ProcessPowerThrottling,
        &state,
        sizeof(state)))
{
    throw std::runtime_error("SetProcessInformation failed");
}

ControlMask indica “quali meccanismi vogliamo controllare”, StateMask indica “accendere o spegnere ciascun meccanismo”. Nell’esempio sopra, abbiamo scelto di controllare entrambi i meccanismi e li abbiamo impostati entrambi su off. In altre parole, dichiariamo di non voler cadere in EcoQoS (più vicino a HighQoS) e di non voler che il sistema ignori le richieste di risoluzione del timer. Al contrario, impostando ControlMask su 0, si ritorna al comportamento predefinito lasciato al sistema operativo.

Per chiamarlo da C#

Se vuoi fare la stessa cosa in C#, la dichiarazione P/Invoke è la seguente.

// C# / .NET 8
using System.Runtime.InteropServices;

internal static class PowerQos
{
    [StructLayout(LayoutKind.Sequential)]
    private struct PROCESS_POWER_THROTTLING_STATE
    {
        public uint Version;
        public uint ControlMask;
        public uint StateMask;
    }

    private const uint PROCESS_POWER_THROTTLING_CURRENT_VERSION = 1;
    private const uint PROCESS_POWER_THROTTLING_EXECUTION_SPEED = 0x1;
    private const uint PROCESS_POWER_THROTTLING_IGNORE_TIMER_RESOLUTION = 0x4;

    // PROCESS_INFORMATION_CLASS 5 (0-based index 4) is ProcessPowerThrottling
    private const int ProcessPowerThrottling = 4;

    [DllImport("kernel32.dll", SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern bool SetProcessInformation(
        IntPtr hProcess,
        int processInformationClass,
        ref PROCESS_POWER_THROTTLING_STATE processInformation,
        uint processInformationSize);

    [DllImport("kernel32.dll")]
    private static extern IntPtr GetCurrentProcess();

    /// <summary>Disabilita sia l'uso a favore del risparmio energetico che l'ignoranza delle richieste di risoluzione del timer.</summary>
    public static void OptOutOfPowerThrottling()
    {
        var state = new PROCESS_POWER_THROTTLING_STATE
        {
            Version = PROCESS_POWER_THROTTLING_CURRENT_VERSION,
            ControlMask =
                PROCESS_POWER_THROTTLING_EXECUTION_SPEED |
                PROCESS_POWER_THROTTLING_IGNORE_TIMER_RESOLUTION,
            StateMask = 0,
        };

        if (!SetProcessInformation(
                GetCurrentProcess(),
                ProcessPowerThrottling,
                ref state,
                (uint)Marshal.SizeOf<PROCESS_POWER_THROTTLING_STATE>()))
        {
            throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());
        }
    }
}

Chiama PowerQos.OptOutOfPowerThrottling(); una volta prima di iniziare il loop sensibile al tempo. L’handle di processo richiede l’accesso PROCESS_SET_INFORMATION, ma con lo pseudo-handle restituito da GetCurrentProcess() per il proprio processo non ci sono problemi.

4.6. Posizionamento CPU / Migrazione core / Riscaldamento

Per il posizionamento della CPU, piuttosto che passare direttamente al blocco su core specifici (affinità hard / blocco CPU), di solito è meglio iniziare con qualcosa di più vicino all’affinità soft: “per favore esegui principalmente su questi core.”

YesNoMisura primaSetThreadIdealProcessor / CPU SetsAbbastanza migliorato?Fermati lìConsidera SetThreadAffinityMask per ultimoControlla anche temperatura / orologi / lunghe percorrenze

Lista di controllo

  • Il posizionamento della CPU viene toccato solo dopo la misurazione
  • Non eseguire subito il blocco su core specifici
  • SetThreadIdealProcessor o CPU Sets provato per primo
  • SetThreadAffinityMask trattata come ultima risorsa
  • Temperatura, orologi e limitazione termica controllati su lunghi periodi
  • Modalità silenziosa / a basso rumore del laptop selezionata

Come ordine di operazioni, questo flusso è sicuro.

  1. Misurare prima
  2. Se necessario, processore ideale / CPU Sets
  3. Se sono ancora necessari miglioramenti, eseguire il pin su nuclei specifici

Sembra che il blocco su nuclei specifici dovrebbe aiutare, ma rimuove le vie di fuga dell’OS, quindi usato casualmente può effettivamente rendere le cose meno flessibili.

4.7. Isolamento dei conducenti / DPC / ISR / Disturbi esterni

Quando “esplode solo il massimo occasionalmente” o “la media è buona ma p99.9 è pessima”, vale la pena sospettare disturbi esterni oltre il proprio codice.

YesNoYesNoYesNoYesNoSi è verificato un picco in ritardo / mancato / massimoAnche il tempo di elaborazione è lungo?Accorciare il percorso attivo / ridurre l'allocazione / rimuovere I / OCi sono picchi DPC / ISR?Controlla USB / Wi-Fi / Bluetooth / GPU / audio / storage / ACPI / aggiornamenti driverErrori di pagina / GC / costi di prima esecuzione?Preassegnare / riscaldare / ridurre la pressione heapBatteria / risparmio energetico / effetti termici?Alimentazione CA / impostazioni di alimentazione / raffreddamento / test di lunga durataScava più a fondo con ETW / WPA / LatencyMon

Lista di controllo

  • I driver attorno a Wi-Fi / Bluetooth / USB / storage / GPU / audio sono stati controllati
  • Rispetto alla sincronizzazione cloud non necessaria, all’indicizzazione e agli aggiornamenti automatici interrotti
  • Testato anche se le cose peggiorano quando ridotte a icona o con il display spento
  • DPC / ISR trend ispezionati con LatencyMon o ETW
  • “La mia elaborazione è pesante” vs “Sono bloccato dall’esterno” esaminati separatamente

5. Misurazione e valutazione

5.1. Cosa registrare

Come minimo, vuoi catturarli.

  • Periodo di tempo programmato
  • Ora di inizio effettiva
  • Orario di fine effettivo
  • Ritardo (quanto ritardo è stato l’inizio rispetto al programma)
  • Tempo di esecuzione
  • Conteggio scadenze non rispettate
  • Conteggio scadenze mancate consecutive
  • Profondità della coda
  • Conteggio delle gocce
  • Utilizzo della CPU
  • Disallineamento per core
  • DPC / ISR punte
  • Difetti di pagina
  • Variazione temperatura / orologio

Guardare solo le medie rende difficile coglierne l’essenza. Ciò che danneggia la produzione è l’occasionale picco di latenza.

5.2. Come leggere p99 / p99.9 / max

Esistono metriche come p99 per osservare la coda lenta. Le medie da sole nascondono occasionalmente un grande ritardo.

Metrico Significato Intuizione oltre 10.000 misurazioni
Nella media Il valore complessivo livellato Le punte vengono sepolte facilmente
p50 Il valore medio Vicino all’atmosfera quotidiana
p95 La soglia in cui inizia a mostrare il 5% più lento Il confine escludendo i 500 più lenti.
p99 La soglia in cui l’1% più lento inizia a mostrare Il confine escludendo i 100 più lenti
p99.9 La soglia in cui lo 0,1% più lento inizia a mostrare Il confine escludendo i 10 più lenti
massimo Il caso peggiore La corsa più lenta

Ad esempio, con:

  • Media: 0,8 ms
  • p99: 1,2 ms
  • p99.9: 3,5 ms
  • massimo: 28 ms

la storia è: di solito veloce, ma con occasionali picchi di grandi dimensioni. Nel Windows ordinario, i veri problemi risiedono quasi sempre in questa coda da p99 a max.

Questo articolo non mostra numeri come prova degli effetti delle configurazioni consigliate. Ritardo e jitter cambiano facilmente con CPU, driver, software residente, impostazioni energetiche e modo di caricare il sistema, quindi i numeri presi da un altro ambiente non possono essere la base per il proprio. Invece, riportiamo la procedura per costruire la stessa tabella nel proprio ambiente.

Procedura minima per calcolare p99 nel proprio ambiente

  1. Nel percorso critico registra solo. Con Stopwatch.GetTimestamp() (in C++ QueryPerformanceCounter) prendi lateness e tempo di esecuzione e scrivili in un array preallocato. Non calcolare medie o ordinamenti qui
  2. Dopo aver fermato la misura, aggrega: ordina e leggi il valore alla posizione percentile
  3. Ripeti cambiando condizioni: prima/dopo il warm-up, AC/batteria, UI in primo piano/minimizzata, con/senza carico di altri processi (vedi 5.4)
  4. Ogni volta che aggiungi una modifica, ripeti la misura nelle stesse condizioni e confronta
// C# / .NET 8. L'aggregazione avviene dopo la misura.
using System.Diagnostics;

// Nel percorso critico scrivi solo nell'array (nessuna allocazione)
long[] latenessTicks = new long[100_000];
int count = 0;

// Esempio: all'interno del ciclo periodico
// latenessTicks[count++] = Stopwatch.GetTimestamp() - scheduledTimestamp;

static double PercentileMs(long[] ticks, int count, double percentile)
{
    long[] sorted = ticks.AsSpan(0, count).ToArray();
    Array.Sort(sorted);

    int index = (int)Math.Ceiling(percentile / 100.0 * count) - 1;
    index = Math.Clamp(index, 0, count - 1);

    return sorted[index] * 1000.0 / Stopwatch.Frequency;
}

// Uso
// Console.WriteLine($"p50={PercentileMs(latenessTicks, count, 50):F3}ms");
// Console.WriteLine($"p99={PercentileMs(latenessTicks, count, 99):F3}ms");
// Console.WriteLine($"p99.9={PercentileMs(latenessTicks, count, 99.9):F3}ms");
// Console.WriteLine($"max={PercentileMs(latenessTicks, count, 100):F3}ms");

Stopwatch.Frequency è il numero di tick al secondo, quindi dividendo per essa e moltiplicando per 1000 si ottiene il tempo in millisecondi. Con pochi campioni p99.9 non ha senso: per parlarne seriamente servono almeno 10.000 campioni, meglio 100.000.

5.3. Con cosa misurare

Il toolkit è abbastanza standard.

  • Misurazione in-app Per prima cosa cattura period / lateness / execution time / queue depth / drop te stesso
  • ETW / WPR / WPA Analizza CPU, cambi di contesto, DPC / ISR, errori di pagina
  • LatencyMon Ottieni un rilevamento delle oscillazioni indotte dal conducente
  • Monitoraggio temperatura / orologio Attenzione agli effetti termici
Misurazione nell'appp50 / p95 / p99 / p99,9 /maxmancati / cadute / profondità della codaETW / WPR / WPAinterruttori di contesto / DPC / ISR / errori di paginaMonitoraggio della temperatura / orologioDare priorità ai miglioramenti

Arrivare fino al WPA richiede un certo sforzo, ma è molto efficace per separare se DPC / ISR sono la causa o se la tua elaborazione è semplicemente pesante.

Come ottenerli e come usarli al minimo

Strumento Come ottenerlo Flusso minimo
WPR / WPA (Windows Performance Toolkit) Selezionare “Windows Performance Toolkit” durante l’installazione di Windows ADK (Windows Assessment and Deployment Kit). Il percorso predefinito è C:\Program Files (x86)\Windows Kits\10\Windows Performance Toolkit Aprire un prompt dei comandi come amministratore, (1) avviare la registrazione con wpr -start CPU, (2) far girare il processo problematico per qualche decina di secondi, (3) fermare con wpr -stop trace.etl "Indagine sulla latenza periodica". Aprire trace.etl con WPA. L’elenco dei profili disponibili si ottiene con wpr -profiles
LatencyMon Scaricarlo dal sito di Resplendence Software. L’edizione Home per uso personale è gratuita Avvia la misurazione e lascia girare il processo target per alcuni minuti. Vengono raccolti il massimo ritardo del timer del kernel e, per ogni driver, tempi di esecuzione di ISR / DPC e hard page fault. Prendi nota del driver che si distacca.

La prima cosa da guardare in WPA è il grafico CPU relativo a tempi di esecuzione di DPC / ISR e context switch. Qui vedrai “quale driver teneva la CPU nei momenti in cui il tuo thread non riusciva a girare”; incrocia questi istanti con i timestamp dei ritardi registrati in 5.1.

Non ci sono screenshot in questo articolo. Poiché l’interfaccia cambia rapidamente tra le versioni, segui i passaggi sopra e verifica sulla schermata reale.

5.4. Disciplina di prova

Un ambiente da banco silenzioso da solo non è sufficiente per i test. Come minimo, vuoi esaminare queste condizioni separatamente.

  • Subito dopo l’avvio, prima del riscaldamento
  • Dopo il riscaldamento
  • Lunghe tirature continue
  • UI in primo piano
  • UI ridotta a icona / quasi nascosta
  • Alimentazione CA
  • Alimentazione a batteria
  • Con carico sulla rete o sul disco

Valutare solo al banco rende facile non notare i problemi che si presentano durante il funzionamento reale. Windows Il comportamento ordinario viene facilmente aggirato dal “come viene utilizzata la macchina”, quindi è importante verificarlo in condizioni vicine all’uso effettivo.

6. Criteri pratici per decidere

La tabella rapida per intervallo periodico è stata anticipata nella 1.1. Qui completiamo le due scelte che quella tabella da sola non riesce a sciogliere.

6.1. Quando giudicare “con Windows ordinario non basta”

Quando serve stare sotto 1 ms per periodi lunghi e con carico elevato, spesso è più veloce decidere di spostare fuori la parte critica dal punto di vista temporale anziché continuare a ottimizzare. Le destinazioni tipiche sono firmware lato dispositivo, controller dedicato, FPGA o RTOS. Non si tratta di sensazioni: il criterio sono i numeri del capitolo 5.

  • Il percorso caldo non può essere accorciato oltre, ma p99.9 e max continuano a superare il requisito
  • I disturbi (DPC / ISR, driver, altri processi) sono la causa e le contromisure lato app non bastano (verifica con 4.7)
  • Il requisito non è più “una rara violazione non rompe nulla”, ma “nessuna violazione è tollerata”

6.2. Quando far coesistere GUI / logging / comunicazione / DB

GUI, log, comunicazione e DB dentro lo stesso processo e lo stesso loop fanno sì che le esigenze di una fase a valle facciano saltare le scadenze di una fase a monte. L’attesa di flush del file, la riconnessione del DB o il ridisegno della UI possono allungarsi di decine di millisecondi. La separazione fast path / slow path di 4.2, estesa fino al confine del processo, è un’opzione valida: se la fase a valle si blocca, la fase a monte continua a rispettare i periodi.

7. Conclusione

Ci sono due premesse a cui vale la pena tenersi strette.

  • L’obiettivo a cui puntare nel Windows ordinario non è una garanzia del hard real-time, ma una configurazione del soft real-time: latenza e jitter ridotti e nessuna interruzione in caso di scadenza non rispettata
  • La vittoria più grande è riordinare il percorso caldo, più che modificare le priorità

Dal punto di vista dell’implementazione, questi sono i risultati:

  • Separare il percorso veloce e quello lento
  • Utilizza code a lunghezza fissa e decidi in anticipo la politica di overflow
  • Misurare con QPC; attendere con eventi / timer di attesa
  • Evitare l’allocazione, il blocco dell’I / O e i blocchi pesanti sul percorso attivo

Dal punto di vista operativo, questi sono i vantaggi:

  • Funziona con alimentazione CA
  • Mantenere una configurazione di alimentazione separata per la produzione
  • Ridurre il carico di elaborazione in background non necessario
  • Valuta con p99 / p99.9 / max e conteggi mancati

Soft real-time su Windows l’ordinario non è deciso solo dalle impostazioni di priorità: se si lavora su progettazione, implementazione, impostazioni di potenza, misurazione e operazioni come questioni separate, è possibile costruire un sistema straordinariamente stabile.

8. Riferimenti

Articoli recenti con gli stessi tag per approfondire argomenti vicini.

Queste pagine collocano l’argomento in un contesto più ampio di servizi e decisioni.

Domande frequenti

Domande che ricorrono nelle consulenze sull’argomento dell’articolo.

Windows può eseguire l'elaborazione in tempo reale?
Windows non è un hard real-time OS, quindi garantire zero scadenze mancate solo con un'app in modalità utente non è realistico. Ciò che puoi ottenere su Windows 10 / 11 ordinario è soft real-time: una configurazione che mantiene bassa la latenza nel caso normale, mantiene piccolo il jitter, non si interrompe quando una scadenza viene occasionalmente mancata e può osservare che l'errore è avvenuto. L'elaborazione periodica da pochi millisecondi a decine di millisecondi, l'audio / video guidato da buffer e i cicli di acquisizione del sensore sono tutti veramente pratici.
Cosa causa latenza e jitter nell'elaborazione periodica su Windows?
Le cause principali sono le priorità dello scheduler e dei thread, DPC e ISR di lunga durata da driver come USB, Wi-Fi, storage e GPU, errori di pagina sul percorso caldo, risoluzione del timer e gestione energetica, migrazione del core e limitazione termica. In particolare, anche quando il codice dell'applicazione funziona correttamente, le circostanze del driver o dell'hardware possono bloccare i thread in modalità utente, quindi il semplice aumento della priorità dell'app in genere non risolve il problema.
Perché Sleep(1) è dannoso per un ciclo periodico di 1 ms?
Sleep(1) non produce un periodo di 1 ms: attende circa 1 ms o più e quindi aggiunge sopra il tempo di esecuzione del lavoro e il superamento dell'attesa si accumula come deriva. Un ciclo periodico è più stabile se guidato da scadenze assolute: aggiorna la scadenza con `next += period`, attendi fino a poco prima (`next - margin`) con un evento del dispositivo o un timer di attesa ad alta risoluzione e utilizza solo una rotazione molto breve per la regolazione fine finale. Il tempo deve essere misurato con QueryPerformanceCounter o Stopwatch in .NET.
Come devo valutare se il mio sistema soft real-time è stabile?
Non fare affidamento sulle medie: ciò che danneggia la produzione è l'occasionale picco di latenza, che le medie nascondono. Registra gli orari di inizio e fine programmati rispetto a quelli effettivi e valuta la coda lenta con p99, p99.9 e max, insieme al conteggio delle scadenze mancate, alla profondità della coda e al conteggio delle cadute. Per l'analisi delle cause principali, utilizza ETW / WPA e LatencyMon per separare se DPC e ISR ti stanno bloccando o se la tua elaborazione è semplicemente pesante e testa in condizioni realistiche, tra cui alimentazione a batteria, finestre ridotte a icona e lunghi cicli continui.

Profilo dell’autore

Pagina di presentazione dell’autore dell’articolo.

Go Komura

Rappresentante di KomuraSoft LLC

Specializzato nello sviluppo di software Windows, nella consulenza tecnica e nell’analisi dei malfunzionamenti, soprattutto nei progetti con sistemi esistenti e guasti difficili da riprodurre.

Torna al blog