In meiner Anwendung soll jede Millisekunde etwas parallel gemacht
werden.
Ich befürchte, ich habe den falschen Ansatz, da ich aus der µC Welt
komme.
Als GUI nutze ich wxWidgets. Ich habe zwei Ansätze, in beiden Fällen
werden die 1 Millisekunde natürlich nicht eingehalten, es sind eher ein
paar 100 Millisekunden Abstand zwischen den Aufrufen. In der
1
loop()
werden wirklich nur ein paar Additionen durchgeführt, keine große
Berechnung.
1. Einen std::thread laufen lassen:
Norbert schrieb:> Bei einem 1ms Intervall sollten aber besser keine GUI Interaktionen im> Thread passieren.
Wenn dieser 1ms Thread etwas af der GUI machen soll, erzeuge ich ein
Event.
EDIT: Geändert. Der Thread braucht ca. 15ms statt 1ms. Keine GUI
Interaktionen, nur ggf. ein Event.
Moin,
ohne es zu wissen, nehme ich mal der TO benutzt ein OS aus Redmond.
Ich habe mir mal eben wxWidgets gezogen und in die Sourcen geschaut.
Intern wird bei Windows (TM) ::Sleep aufgerufen.
Dazu finden sich hier ein paar Hinweise zur Genauigkeit:
https://learn.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-sleep#remarks
Gruß
Olaf
Keine A. schrieb:> EDIT: Geändert. Der Thread braucht ca. 15ms statt 1ms. Keine GUI> Interaktionen, nur ggf. ein Event.
Na ja, wenn der Thread einmal pro ms laufen soll, dann schuldest du ihm
jedes mal 14ms. Das läppert sich aber… ;-)
Nur mal so nebenbei, 14ms sind ja gleich mehrere Ewigkeiten, löst du da
drin das 3n+1 Problem?
Sofern das ganze unter Windows läuft, kann die Timergranularität mit den
sogenannten "Multimediafunktionen" verfeinert werden. Normalerweise
liegt die Granularität bei 10 oder sogar 15 msec, mit der
Win32-API-Funktion timeBeginPeriod kannst Du das in ganzen Millisekunden
auf 1 msec reduzieren.
https://learn.microsoft.com/en-us/windows/win32/api/timeapi/nf-timeapi-timebeginperiod
Harald K. schrieb:> Sofern das ganze unter Windows läuft, kann die Timergranularität> mit den> sogenannten "Multimediafunktionen" verfeinert werden. Normalerweise> liegt die Granularität bei 10 oder sogar 15 msec, mit der> Win32-API-Funktion timeBeginPeriod kannst Du das in ganzen Millisekunden> auf 1 msec reduzieren.
Muss wohl echt ein Windows Ding sein. Selbst mit Python und wxPython ist
die eine Millisekunde bei Linux überhaupt kein Problem (und die CPU Last
nicht wahrnehmbar).
Und das obwohl (Standard)Python und Threading keine besonders
effizienten Freunde sind.
Das hat nichts mit Effizienz, sondern der Frequenz des Timerinterrupts
zu tun. Und die liegt halt auf Windowssystemen üblicherweise bei 100 Hz.
Früher™ war das noch schlimmer, da lag sie bei nur 18.2 Hz.
Keine A. schrieb:> da ich aus der µC Welt> komme.
Willkommen in der Welt der nicht Echtzeit tauglichen Betriebssysteme:-)
Vereinfacht: Wenn du einen Thread für eine bestimmte Zeit Schlafen
schickst, bedeutet das für das OS nur das die angegebenen Zeit die
frühest mögliche Zeit ist wo dein Thread überhaupt mal wieder in der
Warteschlange Platznehmen darf. Und wenn das OS dann irgendwann mal Lust
dazu hat bekommt dein Thread auch mal wieder etwas Rechenzeit zugeteilt.
Je nach Priorität und anderen Aufgaben kann das durchaus mal einige
Millisekunden (zusätzlich) dauern bis du mal wieder dran bist.
Das ganze ist also kein Timer der eine genau definierte Zeit wartet und
danach unverzüglich weiter arbeitet. Windows, Linux und Unix geben sich
da übrigens praktisch nichts, die arbeiten da alle sehr ähnlich.
- https://de.wikipedia.org/wiki/Completely_Fair_Scheduler
- https://de.wikipedia.org/wiki/Prozess-Scheduler
- https://github.com/uu-os-2018/slides/find/master (vorallem die wo es
um scheduling geht)
Norbert schrieb:> Muss wohl echt ein Windows Ding sein. Selbst mit Python und wxPython ist> die eine Millisekunde bei Linux überhaupt kein Problem
LOL
Du hast ja mal definitiv überhaupt keine Ahnung. Was glaubst du, wozu es
die Echtzeit-Patches für den Kernel gibt, wenn Linux das schon von Hause
aus angeblich so problemlos handeln kann?
Harald K. schrieb:> Das hat nichts mit Effizienz, sondern der Frequenz des Timerinterrupts> zu tun. Und die liegt halt auf Windowssystemen üblicherweise bei 100 Hz.> Früher™ war das noch schlimmer, da lag sie bei nur 18.2 Hz.
Naja, das hat sehr wohl auch was mit der Effizienz zu tun. Es gibt (für
Windows genauso wie für Linux) Echtzeit-Patches, die das Task-Switching
in die Größenordnung 1ms bringen. Allerdings: bei beiden OS ist das
erwartungsgemäß mit insgesamt einer deutlichen Verschlechterung der
Leistung des Gesamtsystems verbunden.
Genau deshalb wendet man diese Patches auch nicht für normale
Installationen an.
C-hater schrieb:> Norbert schrieb:>>> Muss wohl echt ein Windows Ding sein. Selbst mit Python und wxPython ist>> die eine Millisekunde bei Linux überhaupt kein Problem>> LOL>> Du hast ja mal definitiv überhaupt keine Ahnung. Was glaubst du, wozu es> die Echtzeit-Patches für den Kernel gibt, wenn Linux das schon von Hause> aus angeblich so problemlos handeln kann?
Glaubst du Clown allen Ernstes das eine MILLISEKUNDE ein
ernstzunehmendes Problem darstellt? Du meine Güte, ich habe so etwas
ständig hier laufen.
Manchmal sind es vielleicht 1020µs, manchmal auch 1050µs. Aber das ist
es dann auch.
Möglicherweise magst du dich auch einmal mit NUMA/Prioritäten/expliziten
Kernzuweisungen usw. beschäftigen.
Du kannst aber auch gerne weiter rumquatschen.
Norbert schrieb:> Glaubst du Clown allen Ernstes das eine MILLISEKUNDE ein> ernstzunehmendes Problem darstellt?
C-Hater hat vollkommen recht.
Ohne PREEMPT_RT ist das Einhalten der Millisekunde pure Glückssache.
MaWin O. schrieb:> C-Hater hat vollkommen recht.> Ohne PREEMPT_RT ist das Einhalten der Millisekunde pure Glückssache.
Es geht nicht um das präzise Einhalten, soviel sollte doch klar sein.
Es ging hier die ganze Zeit um einen 1ms Timerevent.
Und wenn man mit einer seltenen Abweichung im - sagen wir 100µs Bereich
- leben kann.
Schnell hingeklopfter Beispielcode (und ein Log):
1
#!/usr/bin/python3
2
# -*- coding: utf-8 -*-
3
importarray
4
importtime
5
importsignal
6
7
defcb_signal(signum,stackframe):
8
globalidx,end_all
9
arr[idx]=time.time()
10
idx+=1
11
ifidx>=MAX_COLLECT:
12
signal.setitimer(signal.ITIMER_REAL,0,0)
13
end_all=True
14
15
MAX_COLLECT=1000+1
16
end_all=False
17
arr=array.array('d',(0for_inrange(MAX_COLLECT)))
18
idx=0
19
signal.signal(signal.SIGALRM,cb_signal)
20
signal.setitimer(signal.ITIMER_REAL,0.001,0.001)
21
whilenotend_all:
22
time.sleep(.1)
23
24
foriinrange(1,MAX_COLLECT):
25
delta_us=(arr[i]-arr[i-1])*1E6
26
print(f'{1000-delta_us:+8.3f} µs')
Aufruf:
python3 PC_test_timer.py
und dann:
nice -10 python3 PC_test_timer.py
Und wenn das noch nicht reichen mag, kann man auch noch exklusiv einen
Kern reservieren. Dann wird's noch schöner.
Erzählt mir bitte nicht das Dinge nicht gehen, wenn ich sie hier ständig
laufen habe. Nun kann's jeder ausprobieren. Danke.
MaWin O. schrieb:> Norbert schrieb:>> Glaubst du Clown allen Ernstes das eine MILLISEKUNDE ein>> ernstzunehmendes Problem darstellt?>> C-Hater hat vollkommen recht.> Ohne PREEMPT_RT ist das Einhalten der Millisekunde pure Glückssache.
Erfahrungsgemäß funktioniert es ohne PREEMPT_RT mit einer Millisekunde
schon recht gut, wenn nicht gerade noch andere rechenlastige Prozesse
oder eine Auslagerungsorgie läuft. Aber man muss mit ein paar Ausreißern
rechnen. Mit PREEMPT_RT ist die Millisekunde tatsächlich kein Problem.
Und der Prozess muss natürlich für sich realtime-Scheduling aktivieren
und sich gegen Auslagerung sperren - und vorher die Berechtigung dazu
bekommen haben. Weiterhin wartet man nicht für eine bestimmte Dauer,
sondern bis zu einem bestimmten Zeitpunkt, den man dann immer in
Millisekundenschritten hochzählt. Das habe ich seit fast 15 Jahren in
mehreren Systemen erfolgreich so im Einsatz.
Rolf M. schrieb:> Erfahrungsgemäß funktioniert es ohne PREEMPT_RT mit einer Millisekunde> schon recht gut, wenn nicht gerade noch andere rechenlastige Prozesse> oder eine Auslagerungsorgie läuft. Aber man muss mit vereinzelten> Ausreißern rechnen.
Auch wenn man explizit einen Kern nur für diese Anwendung sperrt? Und
die Priorität erhöht (den nice Wert absenkt)? Dafür sind ja nur wenige
Zeilen in einer Konfigurationsdatei einzutragen und man darf das als
normaler User.
Ausreißer? So etwas ist mir noch nie unter gekommen - was aber nicht
heißt das es nicht möglich wäre. Allein der sich dahinter versteckende
Mechanismus würde mich natürlich interessieren.
> Weiterhin wartet man nicht für eine bestimmte Dauer, sondern bis> zu einem bestimmten Zeitpunkt, den man dann immer in> Millisekundenschritten hochzählt.
Genau das macht ›titimer‹, darum hat man manchmal wenige Mikrosekunden
weniger, dann wieder wenige Mikrosekunden mehr.
Norbert schrieb:> Rolf M. schrieb:>> Erfahrungsgemäß funktioniert es ohne PREEMPT_RT mit einer Millisekunde>> schon recht gut, wenn nicht gerade noch andere rechenlastige Prozesse>> oder eine Auslagerungsorgie läuft. Aber man muss mit vereinzelten>> Ausreißern rechnen.>> Auch wenn man explizit einen Kern nur für diese Anwendung sperrt? Und> die Priorität erhöht (den nice Wert absenkt)? Dafür sind ja nur wenige> Zeilen in einer Konfigurationsdatei einzutragen und man darf das als> normaler User.
Man darf als normaler User einen Kern komplett für eine spezifische
Anwendung reservieren? Aber so habe ich das noch nicht getestet.
> Ausreißer? So etwas ist mir noch nie unter gekommen - was aber nicht> heißt das es nicht möglich wäre. Allein der sich dahinter versteckende> Mechanismus würde mich natürlich interessieren.
Woher sie genau kommen, weiß ich nicht, aber ich konnte sie durchaus
messen. Das war aber wie gesagt ohne PREEMPT_RT. Mit dem Preempt-Kernel
waren diese Ausreißer nicht zu sehen. Das hat mir dann auch deutlich
gezeigt, dass Preempt durchaus wichtig ist.
>> Weiterhin wartet man nicht für eine bestimmte Dauer, sondern bis>> zu einem bestimmten Zeitpunkt, den man dann immer in>> Millisekundenschritten hochzählt.>> Genau das macht ›titimer‹, darum hat man manchmal wenige Mikrosekunden> weniger, dann wieder wenige Mikrosekunden mehr.
Ich nutze clock_nanosleep(), das auch zu den Realtime-Funktionen zählt.
Dem kann man sagen, welche "clock" es nehmen soll, und man kann wählen,
ob man relativ oder absolut warten will.
Rolf M. schrieb:> Man darf als normaler User einen Kern komplett für eine spezifische> Anwendung reservieren?
Grins. Na ja, root muss es ihm schon irgendwann mal erlauben…
Genau wie die Prioritätserhöhung.
Core spezifizieren geht ohne Erlaubnis, da es sich ja um ein selbst
auferlegtes weiteres Limit handelt.
> Woher sie genau kommen, weiß ich nicht, aber ich konnte sie durchaus> messen. Das war aber wie gesagt ohne PREEMPT_RT. Mit dem Preempt-Kernel> waren diese Ausreißer nicht zu sehen. Das hat mir dann auch deutlich> gezeigt, dass Preempt durchaus wichtig ist.
Muss ich mir bei Gelegenheit mal bauen, vielleicht macht's einige Dinge
noch einfacher. Wie gesagt, kleine Unschärfen im sub100µs Bereich
beobachte ich auch. Stören aber keine meiner Routinen.
> Ich nutze clock_nanosleep(), das auch zu den Realtime-Funktionen zählt.> Dem kann man sagen, welche "clock" es nehmen soll, und man kann wählen,> ob man relativ oder absolut warten will.
Ja, so wie bei setitimer: ITIMER_REAL,ITIMER_VIRTUAL,ITIMER_PROF
Nur da muss man nicht selber warten und zählen, es gibt einen konstanten
Strom von Signalen die callbacks auslösen.
Norbert schrieb:> sagen wir 100µs Bereich
Dieser Bereich ist PREEMPT_RT vorbehalten.
Norbert schrieb:> Erzählt mir bitte nicht das Dinge nicht gehen, wenn ich sie hier ständig> laufen habe. Nun kann's jeder ausprobieren. Danke.
Du bist ein Schwätzer.
Dass es bei dir funktioniert, beweist rein gar nichts.
Norbert schrieb:> Rolf M. schrieb:>> Man darf als normaler User einen Kern komplett für eine spezifische>> Anwendung reservieren?>> Grins. Na ja, root muss es ihm schon irgendwann mal erlauben…> Genau wie die Prioritätserhöhung.
Ok, das passt schon eher zu dem, wie ich es mir vorstelle.
> Muss ich mir bei Gelegenheit mal bauen, vielleicht macht's einige Dinge> noch einfacher.
Das ist das schöne bei Debian. Da gibt's den immer fertig als Paket.
Einfach apt install linux-image-rt, booten und fertig.
> Ja, so wie bei setitimer: ITIMER_REAL,ITIMER_VIRTUAL,ITIMER_PROF> Nur da muss man nicht selber warten und zählen, es gibt einen konstanten> Strom von Signalen die callbacks auslösen.
Bei clock_nanosleep gibt's noch CLOCK_MONOTONIC, für das es bei
setitimer soweit ich weiß keine direkte Entsprechung gibt. Und ich muss
dann eben nicht den Umweg über einen Signalhandler gehen, sondern
arbeite direkt in einer while-Schleife, die am Ende jedes Durchlaufs
einfach auf den nächsten Tick wartet. Und warten musst du ja auch
irgendwie.
MaWin O. schrieb:> Norbert schrieb:>> sagen wir 100µs Bereich>> Dieser Bereich ist PREEMPT_RT vorbehalten.
So große Abweichungen habe ich bei PREEMPT_RT aber auch noch nicht
gesehen.
MaWin O. schrieb:> Rolf M. schrieb:>> So große Abweichungen habe ich bei PREEMPT_RT aber auch noch nicht>> gesehen.>> Habe ich das behauptet?
Ist das eine Fangfrage?
Schade, dass das hier wieder in ein Gemätzel abdriftet. Was kann ich
praktisch tun, damit es besser läuft?
Ich habe die gleiche Anwendung einmal in Qt und einmal mit wxWidgets
gebastelt. Bei Qt läuft das mit einem QTimer wesentlich besser, bzw. ich
merke keine Verzögerung.
MaWin O. schrieb:> Norbert schrieb:>> sagen wir 100µs Bereich>> Dieser Bereich ist PREEMPT_RT vorbehalten.>> Norbert schrieb:>> Erzählt mir bitte nicht das Dinge nicht gehen, wenn ich sie hier ständig>> laufen habe. Nun kann's jeder ausprobieren. Danke.>> Du bist ein Schwätzer.> Dass es bei dir funktioniert, beweist rein gar nichts.
Ich lese hier seit vielen Jahren mit und habe seit der
Registrierungsumstellung noch nichts geschrieben. Aber für das oben
stehende musste ich mich anmelden, weil das so nicht unkommentiert
stehen bleiben darf.
Ich halte eigentlich sehr viel von MaWin Original, aber so pauschal ist
seine Aussage leider ziemlich falsch.
Ich habe beruflich mit komplexeren Echtzeitregelungssystemen zu tun. Wir
setzen dafür AMD EPYC Server mit viel RAM, cores (große
Matrixmultiplikationen, AMD EPYCs wegen Speicherbandbreite Sensoren ->
Server(MVM) -> Aktoren) ein, sowie 10/25/100 GbE Ethernet, um mit den
Steuerelementen zu kommunizieren.
PREEMPT_RT braucht man nur, wenn man mehrere Prozesse auf dem selben
Kern laufen lassen muss (z.B. weil CPU klein gewählt wegen Energiebedarf
oder Bauteilkosten). Wenn man genügen Kerne hat und sie fest für je
einen Thread reservieren kann, dann ist Echtzeit auch ohne PREEMPT_RT
kein Problem.
Wir senden hier Ethernetpakete mit DPDK raus und haben auch den
Messaufbau zum relativ genauen timestampen (100 GbE Sniffer mit PTP und
PPS). Wir bekommen 0.2 µs Standardabweichung und max. 0.5 µs
Absolutfehler vom Sollzeitpunkt. Über Tage. Fast saturierte 10GbE, Jubmo
packets. Vanilla Kernel 5.15.106. Nimmt man normale UDP sockets statt
DPDK sind es immer noch <10µs vom user Prozess bis zum Kabel. Mit XDP
sockets + BPF könnte man vermutlich auch auf DPDK verzichten, bei gleich
niedriger Latenz.
Man muss Linux entsprechend tunen, dass das geht. Kernel cmdline opts:
- isolcpus=<cores>: auf die Kerne setzen, die isoliert werden sollen.
Das weist den scheduler an, keine User-Tasks auf den Kernen zu
schedulen, aber kernel threads und IRQs usw können schon noch vorkommen.
- irqaffinity=<cores>: auf die anderen Kerne setzen (Komplement) als
die, die isoliert werden sollen. Dadurch werden die ganzen IRQs by
default nicht mehr auf den isolierten Kernen gescheduled. Das sollte man
über die kernel cmdline machen anstatt im userspace, weil es manche IRQs
gibt, die sich später nicht mehr verschieben lassen (AFAIK habe ich das
mal mit nem RAID controller gesehen)
- tsc=reliable: Bei modernen CPUs ist die TSC stabil. Linux schaut
dennoch alle paar ms mal nach und prüft, ob der TSC gut ist. Mit
"reliable" kann man das abschalten.
- Dann haben wir noch nohz_full=<cores>, rcu_nocbs=<cores>, iommu=pt,
aber die sind IIRC gar nicht notwendig.
- cstates kann man auf entsprechende timeouts setzen. Manuell aus der
Applikation oder mit tuned. Aber die bewegen sich IIRC so in 20µs
Bereich.
Die Anwendung im userspace setzt beim Start die CPU affinity von den
threads auf die isolierten Kerne, ein Startup-script schiebt die
Netzwerk IRQs auf isolierte benachbarte cores davon.
Man kann das ganze dann noch weiter spinnen mit hugepages + mlock.
(Wahrscheinlich kann man viele von den Sachen auch über cgroupsv2 setzen
und nicht in der kernel cmdline, haben wir aber noch nicht ausprobiert.)
Prozessprioritäten muss man dann gar nicht nutzen. Wieso auch? Es läuft
ja eh nur ein (gepinnter) Thread auf dem Kern.
Zum überprüfen kann (sollte) man das kernel tracing verwenden. Die
Dateien kann man mit Kernelshark analysieren.
Josef schrieb:> Wir setzen dafür AMD EPYC Server mit
Also wieder ein Beispiel.
Und was sagt es aus? Dass es in deinem Fall funktioniert. Nicht mehr und
nicht weniger.
> PREEMPT_RT braucht man nur, wenn man mehrere Prozesse auf dem selben> Kern laufen lassen muss (z.B. weil CPU klein gewählt wegen Energiebedarf> oder Bauteilkosten). Wenn man genügen Kerne hat und sie fest für je> einen Thread reservieren kann, dann ist Echtzeit auch ohne PREEMPT_RT> kein Problem.
Das ist ganz großer Quatsch mit Soße.
Auch ein Exclusiv-CPU-Task kann aufgehalten werden, wenn er durch
Kernelressourcen blockiert wird, die in !PREEMPT_RT blockierend sind.
> Wir senden hier
Schön.
Was macht PREEMPT_RT? Es liefert einen unterbrechbaren Kernel. D.h. z.B.
dass busy loops im Kernel durch andere Locking-Mechanismen ersetzt
werden. Es liefert Prozessprioritäten, die sogar höher als Kernel
threads sein können. Es liefert threaded IRQs, sodass ein IRQ später
behandelt werden kann, wenn gerade ein hochpriorisierter task
gescheduled werden muss.
Wenn man keine tasks hat, die um die selbe CPU "kämpfen", braucht man
PREEMPT_RT nicht. Wenn man die RT priorities nicht nutzt, braucht man es
eigentlich auch nicht, weil es sich wie ein normales Linux verhält.
PREEMPT_RT ist eine wirklich schöne Sache, wenn man auf limitierten
Systemen arbeiten muss (z.B. nur 1 core, embedded). Aber wenn man für
die Anwendung sowieso genügend cores hat (z.B. dual socket à 64 cores),
dann bringt PREEMPT_RT gar nichts bzw. ist sogar langsamer/hat eine
höhere Latenz, weil threaded IRQs und nicht-spinlocks verwendet werden.
Wie immer gilt: nicht blind glauben, sondern messen!
Wir haben früher PREEMPT_RT verwendet ("wegen der Echtzeit", "normaler
Linux kann das doch gar nicht") und haben aber diesen Mythos durch
Messung und Tuning (und Kontakt mit Linux maintainern) aufklären können.
Wieso sollte es auch anders sein? Wenn man alle Störquellen (andere
tasks, IRQs, TSC watchdog, ...) beseitigt hat, wieso sollte Linux einen
Task von seiner CPU reißen? Um den idle task du schedulen? Ne...
Josef schrieb:> Es liefert threaded IRQs,
Die sind immer vorhanden. RT erzwingt sie für alle Interrupts.
> Wenn man keine tasks hat, die um die selbe CPU "kämpfen", braucht man> PREEMPT_RT nicht.
Ja. Wenn das in deinem Fall so ist, dann ist das in deinem Fall so.
In meiner Kernelversion nimmt mein Task ein Kernel-Spinlock. Plötzlich
ist die CPU blockiert. Was nun?
> Aber wenn man für> die Anwendung sowieso genügend cores hat (z.B. dual socket à 64 cores),> dann bringt PREEMPT_RT gar nichts
Das ist halt Quatsch. Kannst du jetzt gerne noch 10 mal wiederholen.
Bleibt Quatsch.
> Wie immer gilt: nicht blind glauben, sondern messen!
Genau. Aber auch nicht von einem System auf die Welt schließen.
> und haben aber diesen Mythos durch> Messung und Tuning (und Kontakt mit Linux maintainern) aufklären können.
Schön für euren Fall.
> wieso sollte Linux einen> Task von seiner CPU reißen?
Es gibt noch mehr, als den Scheduler.
MaWin O. schrieb:> In meiner Kernelversion nimmt mein Task ein Kernel-Spinlock. Plötzlich> ist die CPU blockiert. Was nun?
Wieso sollte mein Task in kernel space gehen?
In DPDK mache ich keine syscalls und in XDP wären es schnelle read/write
für die flags. Natürlich kann man nicht universell sagen, dass es nie
vorkommen kann, aber für konkrete Systeme kann man das sehr wohl:
Im heißen Pfad gibt es wenige und immer die gleichen syscalls (read,
write). clock_gettime geht über die VDSO, kein syscall.
Speicherallozierung macht man im heißen Pfad nicht. Also wo soll das
spinlock herkommen? Man kann auch einfach das tracing mitlaufen lassen,
solange man sich nicht sicher ist.
MaWin O. schrieb:> Ohne PREEMPT_RT ist das Einhalten der Millisekunde pure Glückssache.
Wenn du einen Nieschenkerneltreiber verwendest, der nicht auf PREEMPT_RT
angepasst wurde, ist das Einhalten der Millisekunde auch unter
PREEMPT_RT pure Glückssache.
Und egal ob PREEMPT_RT oder nicht, die SMIs bleiben (noch nicht im
echten Leben gemessen). In unserem Setup sind wir jetzt aber soweit,
dass wir die PCIe4 Latenzen sehen können, z.B. mehrere NICs auf der
selben Karte aber verschiedene cores/threads, die darauf schreiben. Das
ist für uns ein tolerierbares Hintergrundrauschen.
Josef schrieb:> Man muss Linux entsprechend tunen, dass das geht. Kernel cmdline opts:> - isolcpus=<cores>: auf die Kerne setzen, die isoliert werden sollen.> Das weist den scheduler an, keine User-Tasks auf den Kernen zu> schedulen, aber kernel threads und IRQs usw können schon noch vorkommen.
Vielen Dank für Deine Ausführungen aus der Praxis, diese Methoden
verwende ich auch schon seit einigen Jahren erfolgreich. Habt Ihr auch
schon einmal eine Kombination mit exklusiven "cpusets" experimentiert?
MaWin O. schrieb:> Also wieder ein Beispiel.> Und was sagt es aus? Dass es in deinem Fall funktioniert. Nicht mehr und> nicht weniger.
Ich warte ja immer noch auf eine Gegenbeispiel von dir. Solange das
fehlt, sind deine Aussagen nichts weiter als heiße Luft.
Bisher steht es: 3 Praxis-Beispiele, wo es funktioniert zu ein paar
Rants, die behaupten, dass es eigentlich nicht funktioniert.
Keine A. schrieb:> In meiner Anwendung soll jede Millisekunde etwas parallel gemacht> werden.
Warum?
Welches Ereignis ist so wichtig, dass bei einer PC-Applikation der
eigentliche Ablauf des Programms tausend mal pro Sekunde unterbrochen
werden muss?
> Ich befürchte, ich habe den falschen Ansatz, da ich aus der µC Welt> komme.
Wenn Du uns sagen würdest, was Du eigentlich machen willst, könnten wir
Dir da weiterhelfen...
Josef schrieb:> Wieso sollte mein Task in kernel space gehen?
Ja, wie mehrfach gesagt.
Du hast einen Spezialfall, in dem es geht.
Nicht mehr und nicht weniger.
> Wenn du einen Nieschenkerneltreiber verwendest, der nicht auf PREEMPT_RT> angepasst wurde
Was soll denn da angepasst werden müssen?
Allenfalls sollte man solche Zeitanforderunegen an externe Hardware
auslagern. Sonst wird das eh nichts. Ein PC ist ein
Visualisierungsgeraet, kein Controller. Ein externer Controller kann das
Timing viel besser, zur visualisierung zurueck auf den PC.
Purzel H. schrieb:> Sonst wird das eh nichts. Ein PC ist ein> Visualisierungsgeraet, kein Controller
Mit PREEMPT_RT ist das eben nicht so.
Sicher ist man auch mit PREEMPT_RT nicht ganz unabhängig von der
Hardware. Wenn z.B. ein SMI oder NMI ständig reinhaut, dann kann das OS
da gar nichts machen.
Aber generell sind mit PREEMPT_RT, geeigneter HW und z.B. dem
Deadline-Scheduler Deadlines von 100 µs problemlos und zuverlässig
möglich. Eine 1 ms Deadline is geradezu ewig lang für PREEMPT_RT und
erzeugt einem solchen Kernel keinerlei Schweißperlen auf der Stirn.
Wenn es mit der Deadline-Anforderung deutlich unter 100 µs geht, dann
sollte man tatsächlich so langsam über einen eigenen Controller
nachdenken.
Harald K. schrieb:> mit der Win32-API-Funktion timeBeginPeriod kannst Du das in ganzen> Millisekunden auf 1 msec reduzieren.
Jittert aber wie blöd.
Josef schrieb:> Nieschenkerneltreiber
"Nieschen" wie der Name "Lieschen"? Oder wie die bekannte "Nische"?
Beckhoff hat auch so eine Treiberschicht für sein TwinCAT. Wenn das
läuft (und das tut es automatisch, weil der beim Booten geladen wird),
dann hast du auch mal eine Chance, nach langer Zeit wieder einen
Bluescreen zu sehen.
Und den anderen Kollegen hat es deswegen laufend aus den
Teams-Besprechungen rausgehauen. Jetzt hat er die Beckhoff-SW wieder vom
Office-Rechner deinstalliert und einen eigenen PC dafür aufgesetzt.
Josef schrieb:> Und egal ob PREEMPT_RT oder nicht, die SMIs bleiben (noch nicht im> echten Leben gemessen).
Das soll wohl sehr vom Board abhängig sein. Teilweise werden darüber in
dem BIOSen irgendwelche Work-Arounds für Hardware-Bugs integriert.
Deshalb macht OSADL Messungen mit aller möglicher Hardware, um solche
Problemkinder zu finden.
Lothar M. schrieb:> Josef schrieb:>> Nieschenkerneltreiber> "Nieschen" wie der Name "Lieschen"? Oder wie die bekannte "Nische"?
Vielleicht wenn jemand niest: "Ich habe ein Nieschen gemacht". 😉
Sheeva P. schrieb:> Vielen Dank für Deine Ausführungen aus der Praxis, diese Methoden> verwende ich auch schon seit einigen Jahren erfolgreich. Habt Ihr auch> schon einmal eine Kombination mit exklusiven "cpusets" experimentiert?
Wie weiter oben schon geschrieben, haben wir das noch nicht ausprobiert
aber wie haben es vor. Eigentlich sollte es funktionieren, aber testen
muss man es trotzdem. Der Vorteil wäre, dass man nicht ab Boot die Cores
für normale Nutzung im System verliert (Software auf einem 128 Kerner
bauen ist schon schön), sondern dynamisch mit den cpusets einschalten
kann, wann man sie isoliert/exklusiv braucht.
Habe was ähnliches gestern mit C# ausprobiert, einmal mit einem endlos
loopenden backgroundworker + sleep,
https://learn.microsoft.com/en-us/dotnet/api/system.componentmodel.backgroundworker?view=net-7.0
und einmal mit einem timer, der regelmäßig feuert,
https://learn.microsoft.com/en-us/dotnet/api/system.threading.timer?view=net-7.0.
Beides war recht ernüchternd.
Der Timer hat einen overhead von ~8-12 ms pro Event - man kann einen
Offset, der vermutlich nur für mein System gilt abziehen, damit wird die
Genauigkeit bei periodischen Triggern etwas höher - aber kleiner als 15
ms ohne dass irgendwas in dem callback gemacht wird, kam ich nicht.
Ein endlos (lang) laufender BGW ist eigentlich nicht empfohlen (wird
aber häufig auf SO mit einem cancellation token gezeigt), insbesondere,
wenn dort ein Sleep vorkommt, um timings zu simulieren - funzt aber
besser als die Timer Lösung. Auch da gibt es einige ms overhead, die man
offenbar abziehen kann. Minimale sample time (Sleep(1)) war dann so bei
5-8 ms.
Hätte auch gedacht da geht mehr.
Der PC (Windows 10, dicke CPU) hat sich in allen Varianten gelangweilt,
instrumentation hat gezeigt, dass die meiste Zeit im Kernel drauf geht -
ich denke schneller ist das Kontextwechseln einfach nicht.
Edit: https://stackoverflow.com/a/73535286/3620376 kommt auf ähnliche
Zahlen (5-8 ms), offenbar gibt es 15 ms Zeitscheiben, interessant.
Naja, Windows ist halt immer noch kein echtes Multitasking- System.
Dank der neuen Prozessoren und mit mehr Kernen ist es seit
dem Anfang schon ziemlich nahe dran gekommen, aber halt
immer noch nicht ganz (Zeitscheiben).
Deshalb denke ich, daß da immer ein paar ms bleiben, egal
wie man es nun anstellt.