Forum: Compiler & IDEs Funktion mit gleichbleibender Ausführungszeit


von Walter T. (nicolas)


Lesenswert?

Hallo zusammen,

ich habe eine relativ einfache Funktion, die einen analogen Wert 
klassiert:
1
// Mittelwerte der einzelnen Tastendruecke abfallend sortiert
2
#define N_KEYVALUE 6
3
static const uint8_t keyvalue[N_KEYVALUE] = {255,159,101,63,24,0};
4
5
6
/* Gedrueckte Taste an Widerstandsreihe erkennen
7
 * aval: Analoger Wert
8
 * return: Klassierter Wert */ 
9
uint8_t decodeAnalogKey(uint8_t aval)
10
{
11
  uint8_t thresh, i, dummy;
12
  uint8_t keystate = N_KEYVALUE-1;
13
      
14
  // Die Schleife wird immer gleichoft durchlaufen, egal welche Taste
15
  // gedrueckt ist
16
  for(i = 0; i<N_KEYVALUE-1; i++) {
17
        
18
    // Division durch 2 vor der Addition erhoeht Ungenauigkeit auf
19
    // 2, bleibt aber immer im Wertebereich uint8_t.
20
    thresh = keyvalue[i]/2+keyvalue[i+1]/2;
21
        
22
    if(aval < thresh ) {
23
      // Diese Zuweisung erfolgt unterschiedlich oft, je nachdem
24
      // welche Taste gedrueckt ist
25
      keystate = i;
26
    }
27
    else {
28
                    // Wird wegoptimiert, zeigt aber, was ich meine
29
      dummy = i;
30
    }
31
  }
32
  
33
  return keystate;
34
}

Jetzt will ich dafür sorgen, daß diese Funktion immer möglichst gleich 
lange zur Ausführung benötigt, egal mit welchem Eingabewert sie 
aufgerufen wird.

In der bestehenden Form ist der Unterschied zwischen einzelnen Aufrufen 
bis zu fünfmal dem Sprung + Zuweisung, und die dummy-Variable wird 
ohnehin wegoptimiert.

Gibt es unter C überhaupt eine sinnvolle Möglichkeit, eine Funktion 
konstanter Laufzeit zu implementieren und wenn ja: wie?

Viele Grüße
W.T.

von Peter D. (peda)


Lesenswert?

Zu Anfang einen Timer aufsetzen und am Ende in einer Schleife drauf 
warten, bis der Timer abgelaufen ist.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Starte zu Beginn der Funktion einen Timer und warte am Ende der Funktion 
darauf, dass dieser Timer abläuft.

von Peter D. (peda)


Lesenswert?

Warum machst Du die Berechnung der Schwellen nicht gleich in der 
Tabelle?

Beitrag "Tastenmatrix auslesen über nur 2 Leitungen"

von Walter T. (nicolas)


Lesenswert?

Peter Dannegger schrieb:
Frank M. schrieb:
> Starte zu Beginn der Funktion einen Timer und warte am Ende der Funktion
> darauf, dass dieser Timer abläuft.

Ich hab's schon befürchtet...


Peter Dannegger schrieb:
> Warum machst Du die Berechnung der Schwellen nicht gleich in der
> Tabelle?


Reine Faulheit - der µC langweilt sich in dieser Anwendung eh zu Tode. 
So kann ich einfach alle Tasten mal drücken, den Rohwert auf dem Display 
anzeigen lassen und dann direkt in die Tabelle tippen.

Die Frage nach konstanter Laufzeit ist auch nur aus Neugier und weniger 
aus Nutzen.

Und ich sehe, Du hast auch hierfür mal wieder eine fertige Library 
geschrieben und veröffentlicht. Sehr schön.

: Bearbeitet durch User
von Guest (Gast)


Lesenswert?

Würde nicht statt der Dummy-Zuweisung
keystate = N_KEYVALUE-1;
ausreichen?
Dies sollte nicht wegoptimiert werden, bin aber nicht sicher, ob das 
schneller geht, da kein Variablenzugriff erfolgt.

von Walter T. (nicolas)


Lesenswert?

Guest schrieb:
> Würde nicht statt der Dummy-Zuweisung
> keystate = N_KEYVALUE-1;
> ausreichen?

Das würde die Laufzeit ändern - aber es würde nicht mehr das Gleiche aus 
der Funktion herauskommen.

von Guest (Gast)


Lesenswert?

Walter Tarpan schrieb:
> Guest schrieb:
>> Würde nicht statt der Dummy-Zuweisung
>> keystate = N_KEYVALUE-1;
>> ausreichen?
>
> Das würde die Laufzeit ändern - aber es würde nicht mehr das Gleiche aus
> der Funktion herauskommen.

Stimmt. Ist wohl noch zu früh..

von Markus F. (mfro)


Lesenswert?

Du könntest dummy und keystate volatile deklarieren, dann sollte die 
Laufzeit konstant werden.

Effizienter würd's dadurch natürlich nicht.

von Fpgakuechle K. (Gast)


Lesenswert?

Walter Tarpan schrieb:

> Gibt es unter C überhaupt eine sinnvolle Möglichkeit, eine Funktion
> konstanter Laufzeit zu implementieren und wenn ja: wie?

Konstante Laufzeit = konstanter Code (bei jedem Programmaufruf wird der 
selbe code durchlaufen
-> keinerlei Programmverzweigung (while/for/if/switch) verwenden
-> loop unrooling
-> ...


Alternativ kann man dafür sorgen das das aufrufende programm immer zur 
selben zeit weitermacht:

//WDT/timer etc aktivieren
//"konstante" Funktion aufrufen
//endless loop|sleep
//Einsprung nach WDT-IRQ


MfG,

von P. M. (o-o)


Lesenswert?

Markus F. schrieb:
> Du könntest dummy und keystate volatile deklarieren, dann sollte die
> Laufzeit konstant werden.

Gefährlicher Ansatz. Man weiss nie genau, was der Compiler sonst noch 
optimieren könnte oder ob die verwendeten Sprungbefehle alle gleich 
viele Taktzyklen brauchen.

Die einzig saubere Lösung IST hier der Timer. Alles andere kommt nur 
dann in Frage, wenn man haargenau weiss, was der Compiler und der 
Prozessor daraus machen UND sich sicher ist, dass dies für alle 
Zielplattformen gilt. Aber dann schreibt man sowas sowieso besser in 
Assembler.

von Dumdi D. (dumdidum)


Lesenswert?

Timer hat den Nachteil, das man eine bestimmte Zeit festsetzt, Jahre 
spaeter einen Parameter aendert, die Funktion dann laenger braucht als 
der Timer und dann bekommt man die schoensten Fehler. Also wenn timer, 
dann muss auch eine Fehlermeldung weitergegeben werden falls er schon 
abgelaufen ist. Zudem scheint es ja hier auf einzelne Taktzyklen 
anzukommen, dies ist m.M.n nicht oder nur mit hohem Aufwand mit einem 
Timer machbar.

Moegliche Alternative: Funktion in eigene Datei und ohne Optimierung nur 
diese Datei kompilieren.

von P. M. (o-o)


Lesenswert?

Dumdi Dum schrieb:
> Timer hat den Nachteil, das man eine bestimmte Zeit festsetzt, Jahre
> spaeter einen Parameter aendert, die Funktion dann laenger braucht als
> der Timer und dann bekommt man die schoensten Fehler.

Stimmt.

Dumdi Dum schrieb:
> Moegliche Alternative: Funktion in eigene Datei und ohne Optimierung nur
> diese Datei kompilieren.

Und auch hier: Was passiert, wenn beim Linken nochmals optimiert wird?

Generell muss man zu der Frage sagen: Kommt drauf an, was du genau 
willst. Und eine immer sichere Lösung für das Problem gibt es ganz 
sicher nicht. Du wirst den Ansatz immer für den konkreten Zweck anpassen 
und prüfen müssen.

von Stefan (Gast)


Lesenswert?

Ich frag mich ehr warum eine Funktion zur Tastenauswertung immer die 
gleiche Ausführungszeit braucht.

von Bernd K. (prof7bit)


Lesenswert?

Stefan schrieb:
> Ich frag mich ehr warum eine Funktion zur Tastenauswertung immer die
> gleiche Ausführungszeit braucht.

Z.B. damit Wim van Eck nicht aus mehreren Metern Entfernung hören kann 
welche Tasten am Geldautomaten gerdrückt wurden.

von Markus F. (mfro)


Lesenswert?

P. M. schrieb:
> Gefährlicher Ansatz. Man weiss nie genau, was der Compiler sonst noch
> optimieren könnte oder ob die verwendeten Sprungbefehle alle gleich
> viele Taktzyklen brauchen.
>
> Die einzig saubere Lösung IST hier der Timer.

Gefährlich? Man kann's auch übertreiben.

Ich habe nicht den Eindruck, daß es hier unbedingt auf die Mikrosekunde 
ankommt (in anderen Fällen mag das anders sein).

Auch der Timer ist - je nach Auflösung - nicht unbedingt die Garantie 
dafür, daß die Laufzeiten exakt konstant bleiben. Je nachdem, wo der 
Programmzähler beim Ablaufen des Timers in der Abfrageschleife grad 
steht, kann's auch hier Abweichungen geben.

Wenn man's denn unbedingt ganz genau haben will, wird man um Assembler 
und Taktzyklenzählen nicht rumkommen.

von Fpgakuechle K. (Gast)


Lesenswert?

Stefan schrieb:
> Ich frag mich ehr warum eine Funktion zur Tastenauswertung immer die
> gleiche Ausführungszeit braucht.

Es sind schon Patienten verstrahlt worden weil der User schneller tippte 
als das System erlaubt:

https://de.wikipedia.org/wiki/Therac-25#Programmfehler

MfG,

von Dumdi D. (dumdidum)


Lesenswert?

P. M. schrieb:
> Und auch hier: Was passiert, wenn beim Linken nochmals optimiert wird?

Linker optimieren nicht. (Maximal schmeißen sie nicht genutzte Bereiche 
weg. Der Bereich hier wird jedoch genutzt, wenn er nicht genutzt würde 
dürfte er natürlich auch weggeschmissen werden.)
Hast Du ein Beispiel von einem Linker das .obj noch optimiert?

von Markus F. (mfro)


Lesenswert?

Dumdi D. schrieb:
> Linker optimieren nicht.

Tun sie sehr wohl. Und nicht erst seit gestern. Die gcc Toolchain kann's 
seit 2005:
 http://hubicka.blogspot.de/2014/04/linktime-optimization-in-gcc-1-brief.html

Aber auch das dürfte bei diesem Beispiel keine Rolle spielen, da gibt's 
nach dem Compilieren nix mehr zu optimieren.

von Peter D. (peda)


Lesenswert?

Fpga K. schrieb:
> https://de.wikipedia.org/wiki/Therac-25#Programmfehler

Das ist zwar OT, aber wenn man die Programmierfehler liest, kann man aus 
heutiger Sicht nur mit dem Kopf schütteln.
Aus meiner Sicht sind das eindeutig Zeichen, daß der Programmierer den 
Code direkt in die Tastatur gehämmert hat.
Es fehlt einfach die Planung (Programmablaufplan). So einen PAP kann man 
viel leichter auf logische Fehler überprüfen und vor allem, das können 
auch nicht-Programmierer.
Auch zeigt es schön die Risiken des Multitasking, wenn man die dadurch 
wegfallende deterministische Ausführung nicht berücksichtigt.
Beim Multitasking ist nichts atomar, das muß man sich immer wieder in 
den Kopf hämmern. Ansonsten darf man es nicht benutzen.

von Fpgakuechle K. (Gast)


Lesenswert?

Peter D. schrieb:
> Fpga K. schrieb:
>> https://de.wikipedia.org/wiki/Therac-25#Programmfehler
>
> Das ist zwar OT, aber wenn man die Programmierfehler liest

IMHO ist das schon On und nicht Offtopic. In älteren WP-Beschreibung 
wurde der Fehler als "schneller getippt als vorgesehen" beschrieben. 
Benutzt man nun eine (Tastatur-)Routine mit garantierter Laufzeit 
vereinfacht man die (Test-) Szenarien in dem man solche Race-Conditions 
von vornherein ausschliesst. Genau darin kann man den Sinn sehen ->

Stefan schrieb:
> Ich frag mich ehr warum eine Funktion zur Tastenauswertung immer die
> gleiche Ausführungszeit braucht.

https://de.wikipedia.org/wiki/Race_Condition

MfG

von Falk B. (falk)


Lesenswert?

@Peter Dannegger (peda)

>> https://de.wikipedia.org/wiki/Therac-25#Programmfehler

>Das ist zwar OT, aber wenn man die Programmierfehler liest, kann man aus
>heutiger Sicht nur mit dem Kopf schütteln.

Nicht nur aus heutiger! Auch in den 80er Jahren gab es solide 
Programmierer und Leute, die wissen was sie tun! Dort hat man 
anscheinend einen totalen Amateur rangelassen und keinerlei 
Qualitätssicherung betrieben. Und das bei einem nuklearmedizinischen 
Gerät! Irre!
Heute würde man für jeden Toaster mehr CE-Prüfungsaufwand betreiben 
müssen!

von Falk B. (falk)


Lesenswert?

@ Fpga Kuechle (fpgakuechle) Benutzerseite

>Benutzt man nun eine (Tastatur-)Routine mit garantierter Laufzeit
>vereinfacht man die (Test-) Szenarien in dem man solche Race-Conditions
>von vornherein ausschliesst. Genau darin kann man den Sinn sehen ->

???

>> Ich frag mich ehr warum eine Funktion zur Tastenauswertung immer die
>> gleiche Ausführungszeit braucht.

>https://de.wikipedia.org/wiki/Race_Condition

Also wenn dir schon eine einfache Routine zur Tastaturauswertng Race 
Conditions beschert, dann ist wohl nix mehr zu retten!

In wieviel Fällen hat High level Software (und das ist auch C) eine 
konstante Laufzeit? In wie vielen Fällen wird explizit durch Tuning in 
Funktionen erreicht?

Gegen Race Conditions gibt es ganz andere, sinnvolle Mittel! Funktionen 
mit konstanter Laufzeit sind es in den seltensten Fällen.

von Peter D. (peda)


Lesenswert?

Fpga K. schrieb:
> https://de.wikipedia.org/wiki/Race_Condition

Race-Conditions und Laufzeit haben nichts miteinander zu tun.

Eine längere Laufzeit eines kritischen Teils kann aber dazu führen, daß 
eine Race-Condition öfter eintritt und damit schneller debugt werden 
kann.
Bzw. das Vertrödeln von Zeit in einer unkritischen Warteschleife kann 
die Race-Condition wiederum seltener machen.

Eine Race-Condition hat man erst dann gefunden, wenn man den logischen 
Fehler auch plausibel erklären kann.
Keinesfalls sollte man aber denken, daß durch planloses Umschreiben des 
Codes die Race-Condition magisch verschwindet!

von Fpgakuechle K. (Gast)


Lesenswert?

Peter D. schrieb:
> Fpga K. schrieb:
>> https://de.wikipedia.org/wiki/Race_Condition
>
> Race-Conditions und Laufzeit haben nichts miteinander zu tun.
>

Doch. In Digitalschaltungen wie bspw. in FPGA's löst man race conditions 
insbesonders die dadurch entstehenden Glitches in dem man die Logik 
taktet also das Berechnungsergebnis erst nach eine Mindestlaufzeit 
(Taktperiode)
abspeichert, einer Mindestzeit die ausreichend um die race conditions zu 
klären.
https://en.wikipedia.org/wiki/Race_condition#Electronics

Peter D. schrieb:
>Keinesfalls sollte man aber denken, daß durch planloses Umschreiben des
>Codes die Race-Condition magisch verschwindet!

Das ist keinesfalls planlos, der dahinter stehende Plan ist die 
Verringerung der Komplexität zu einem beherrschbaren Level.

Mit dem erzwungenen Gleichlauf von Routinen nimmt man Komplexität aus 
dem System - man muss das system nicht für alle möglichen Eingangsströme 
auslegen sondern nur noch für eine begrenzte Untermenge. Man könnte auch 
Paralleln zum Entprellen ziehen.

MfG,

von Peter D. (peda)


Lesenswert?

Fpga K. schrieb:
> Doch. In Digitalschaltungen wie bspw. in FPGA's löst man race conditions
> insbesonders die dadurch entstehenden Glitches in dem man die Logik
> taktet

Du schweifst vom Thema ab.
Wir waren hier bei CPUs und die sind immer getaktet.
Es ging auch nicht um Glitches, sondern um Race-Conditions.

Eine Race-Condition ist, wenn zwei Tasks die selben(n) Variable(n) 
bearbeiten. Man muß genau diese Stelle finden und dann den Zugriff 
atomar machen.

Planloses Umschreiben des Codes macht aber niemals etwas atomar und löst 
damit die Race-Condition nicht. Es kann aber erheblich die 
Warscheinlichkeit ändern und damit vorgaukeln, man hätte das Problem 
gelöst.

Geht der Fehler nicht weg, sondern ändert sich nur dessen Häufigkeit, 
ist das ein sicheres Zeichen dafür, daß man an der falschen Stelle 
gesucht hat.


Wenn man so will, ist das "atomar" gleichbedeutend mit der 
Synchronisation bei FPGAs. Ein Task wird solange verzögert, bis die 
andere ihren atomaren Block beendet hat.

: Bearbeitet durch User
von Fpgakuechle K. (Gast)


Lesenswert?

Peter D. schrieb:
> Fpga K. schrieb:

> Eine Race-Condition ist, wenn zwei Tasks die selben(n) Variable(n)
> bearbeiten. Man muß genau diese Stelle finden und dann den Zugriff
> atomar machen.

Ich beziehe mich auf die auch oben verlinkte beschreibung des Fehlers 
als Folge eine racer condition:

"The software interlock could fail due to a "race condition". The defect 
was as follows: a one-byte counter in a testing routine frequently 
overflowed; if an operator provided manual input to the machine "at the 
precise moment" that this counter overflowed, the interlock would fail.

aus: https://en.wikipedia.org/wiki/Therac-25#Problem_description

Die Ursache war also das der overflow und die Operatoreingabe 
gleichzeitig auftrat.
Dies kann man verhindern in dem die Maximaldauer des einen prozesses auf 
einen Wert kleiner als als die Minimaldauer des anderen prozesses (bspw 
Tastatureingabe) begrenzt.

MfG,

von Peter D. (peda)


Lesenswert?

Fpga K. schrieb:
> Dies kann man verhindern in dem die Maximaldauer des einen prozesses auf
> einen Wert kleiner als als die Minimaldauer des anderen prozesses (bspw
> Tastatureingabe) begrenzt.

Das ist ein Spiel mit dem Feuer, bzw. bei Multitasking schlichtweg nicht 
möglich.
Der Scheduler verteilt die CPU-Zeit auf alle Tasks gleichmäßig bzw. nach 
ihrer Priorität.
Wann eine Task durchlaufen wurde, ist also rein zufällig und steht auch 
in keiner Beziehung zu anderen Tasks.

Und die Dauer von Nutzereingaben kann man generell nicht bestimmen, da 
ist jeder Mensch eben anders.

von Falk B. (falk)


Lesenswert?

@ Fpga Kuechle (fpgakuechle) Benutzerseite

>Die Ursache war also das der overflow und die Operatoreingabe
>gleichzeitig auftrat.
>Dies kann man verhindern in dem die Maximaldauer des einen prozesses auf
>einen Wert kleiner als als die Minimaldauer des anderen prozesses (bspw
>Tastatureingabe) begrenzt.

Das halte ich für einen gefährlichen Irrtum! Das ist genauso "schlau", 
wie ein Timing einer Digitlaschaltung mit RC-Gliedern hinzupfuschen!

Erstens darf kein Overflow auftreten, egal was passiert. Und zweitens 
muss bei so einer Maschine ein einzelner, allein verantwortlicher 
Prozess/Funktion existieren, der ALLE Parameter irgendwann mal 
einfriert, dann prüft und entsprechend verarbeitet. So wie es 
beschrieben wurde, schien das alles recht lax verzaht gewsesen zu sein 
und diverse Prozesse konnten on the fly diverse Parameter immer wieder 
ändern. Dort ist MASSIV gepfuscht worden. Angefangen beim Design bis zur 
Qualitätssicherung! Und das hätte man auch mit Funktionen im konstanter 
Laufzeit nicht verhindert!

von Fpgakuechle K. (Gast)


Lesenswert?

Falk B. schrieb:
> @ Fpga Kuechle (fpgakuechle) Benutzerseite
>
>>Die Ursache war also das der overflow und die Operatoreingabe
>>gleichzeitig auftrat.
>>Dies kann man verhindern in dem die Maximaldauer des einen prozesses auf
>>einen Wert kleiner als als die Minimaldauer des anderen prozesses (bspw
>>Tastatureingabe) begrenzt.
>
> Das halte ich für einen gefährlichen Irrtum!

Wo steckt da der Irrtum?

T1 <= Ta # Maximaldauer
T2 >= Tb # Minimaldauer

Ta < Tb  # Parameterbedingung

-> T1 != T2



> Erstens darf kein Overflow auftreten, egal was passiert.
? Nö Overflow ist ein propates Mittel um Fehlzustände zu erkennen,
was nicht passieren darf ist das ein overflow der ein Fehlzuständ 
signalisiert ignoriert oder ohne korrekte Behandlung zurückgesetzt wird 
-> das scheint hier passiert zu sein.

> ...
> Und das hätte man auch mit Funktionen im konstanter
> Laufzeit nicht verhindert!

Hatt ja auch keiner behauptet. Es ist ja nur darauf hingewiesen worden 
das eine Tastaturroutine mit konstanter Laufzeit das systemdesign 
dahingehend vereinfacht das keine zu schnelle Eingaben passieren können. 
Halt wie der Tiefpass bei der AD Wandlung die Aliaseffekte verhindert. 
Nimm es einfach als eine HAL die Unterschiede in der Tippgeschwindigkeit 
wegbügelt.

UART ohne built-in-FIFO wie der 8250 wären auch ein Grund für eine 
Tastaturroutine mit konstanter Laufzeit.

Das man dennoch vor dem Schuß sicherstellen muß das die Kanone nicht auf 
tödliche Dosis gestellt ist, bleibt davon unberührt.

MfG,

von Peter D. (peda)


Lesenswert?

Fpga K. schrieb:
> Wo steckt da der Irrtum?
>
> T1 <= Ta # Maximaldauer
> T2 >= Tb # Minimaldauer
>
> Ta < Tb  # Parameterbedingung

Darin, daß Du eine CPU mit einem FPGA verwechselst.
Die Tasks laufen nicht gleichzeitig ab, sondern ineinander 
geschachtelt nacheinander. Ein CPU kann immer nur nacheinander 
ausführen. Damit gibt es keinerlei Zeitbezug, der geprüft und 
eingehalten werden kann.
Alle Tasks kriegen vom Scheduler in jeder Loop mindestens einmal 
CPU-Zeit zugeteilt, das ist der einzige Zusammenhang.

Außerdem kann man die Minimalzeit kaum ermitteln. Wenn eine Task wenig 
zu tun hat, sind das vielleicht 10 CPU-Tyklen. Die Maximalzeit kann 
durchaus 10.000 mal länger sein. Damit sind Deine Bedingungen quasi 
undurchführbar.

Und der Programmierer, der guten Gewissens absichtlich CPU-Zeit 
verplempert, muß erst noch geboren werden.

: Bearbeitet durch User
von Walter T. (nicolas)


Lesenswert?

Peter D. schrieb:
> Und der Programmierer, der guten Gewissens absichtlich CPU-Zeit
> verplempert, muß erst noch geboren werden.

Naja, z.B. im Krypto-Bereich wird ja schon darauf geachtet, daß z.B. 
eine Routine immer gleichviel Zeit benötigt, egal wieviele Ziffern einer 
PIN oder Stellen eines Schlüssels richtig oder falsch sind - um 
Seitenkanalangriffe zu erschweren.

Oder ich kann mir einen Timer sparen, wenn die Berechnung einer 
Stellgröße immer gleichlang dauert - dann kann ich den IRQ des 
A/D-Wandlers direkt mitbenutzen, ohne daß die Ausgabe allzusehr jittert.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Fpga K. schrieb:

> Ich beziehe mich auf die auch oben verlinkte beschreibung des Fehlers
> als Folge eine racer condition:
>
> "The software interlock could fail due to a "race condition". The defect
> was as follows: a one-byte counter in a testing routine frequently
> overflowed; if an operator provided manual input to the machine "at the
> precise moment" that this counter overflowed, the interlock would fail.
>
> aus: https://en.wikipedia.org/wiki/Therac-25#Problem_description
>
> Die Ursache war also das der overflow und die Operatoreingabe
> gleichzeitig auftrat.

Kann man so nicht sagen.
Im Wikipedia Artikel ist ein Link zu einem PDF mit einer etwas genaueren 
Beschreibung der Probleme. IMHO ist die Darstellung im Wiki etwas 
verzerrend verkürzt.

Das Hauptproblem, so wie ich das sehe, war hier, dass man sich nicht 
überlegt hat, wie Benutzereingaben ablaufen sollen.

Wenn Werte eingestellt werden, der Benutzer danach auf 'Los' drückt und 
danach kann ich die Werte immer noch verändern, ohne dass ich zuerst mal 
die Maschine stoppen muss, dann ist da im Benutzerinterface was schief 
gelaufen.
Wenn ich das schon lese: das Programm überwacht ob der Cursor rechts 
unten ist bzw. ob er das Ende einer Zeile erreicht hatte", dann läuft es 
mir kalt runter.
Aber, das war in der damaligen Zeit noch so. Die wurde noch von der IBM 
Systematik des Ausfüllens von Masken dominiert. Nur: wenn man 
realisiert, dass der Benutzer an einer Maske was verändert hat, dann 
muss die *komplette' Maske neu ausgewertet werden. Mit derartigen 
'dieses und jenes hat sich verändert also muss auch nur dieses und jenes 
intern verändert werden' schiesst man sich immer nur selbst ins Knie. 
Spätestens wenn dann in der Programmlogik intern noch Dinge im 
nachhinein voneinander abhängig gemacht werden, sind Fehler 
vorprogrammiert. Und wenn die Maschine erst mal läuft, dann sind alle 
Eingaben gesperrt und fix. Man kann ja immer noch 2 Sets an Werten 
haben. Die einen die der Benutzer zu Gesicht bekommt und die andere, mit 
der die Maschine arbeitet. Beim Start wird von einem Set ins andere 
übernommen. Aber im bereits angelaufenen Betrieb, noch dazu in einem 
sicherheitskritischen Bereich, darf es nicht vorkommen, dass der 
Benutzer noch Arbeitsparameter ändern kann. Eine Änderung bedeutet: 
Maschine stoppt und der ganze Prozess läuft von vorne weg erneut los. 
Inklusive Prüfung der kompletten Eingangswerte.
Das ganze war eine Fehldesign.

Und auch: Späte 70-er Jahre, frühe 80-er Jahre. Das war die Zeit, als 
man jedem, der irgendwas mit Physik oder Technik zu tun hatte, in die 
Programmierung steckte. Tausende Physik-Lehrer können ein Lied davon 
singen, dass sie im nächsten Semester plötzlich für den Informatik 
Unterricht zuständig waren. Wer "F ist m mal a" sagen konnte und wem das 
dann auch noch was sagte, wurde zum Informatiker befördert.

: Bearbeitet durch User
von Fpgakuechle K. (Gast)


Lesenswert?

Peter D. schrieb:
> Fpga K. schrieb:
>> Wo steckt da der Irrtum?

> Darin, daß Du eine CPU mit einem FPGA verwechselst.
> Die Tasks laufen nicht gleichzeitig ab, sondern ineinander
> geschachtelt nacheinander. Ein CPU kann immer nur nacheinander
> ausführen. Damit gibt es keinerlei Zeitbezug, der geprüft und
> eingehalten werden kann.

???
time(), uptime(), clock() alles Zeitnormale auf die jeder Task zugreifen 
kann. Ebenso sleep() resp. usleep() damit kann jeder task eine beliebige 
"laufzeit" anfordern.

> Außerdem kann man die Minimalzeit kaum ermitteln. Wenn eine Task wenig
> zu tun hat, sind das vielleicht 10 CPU-Tyklen. Die Maximalzeit kann
> durchaus 10.000 mal länger sein. Damit sind Deine Bedingungen quasi
> undurchführbar.
 Also meines Wissens arbeiten timesharing machinen mit festen timeslots
 Danach wird gewechselt. Die zeit für diesen context switch ist 
ermittelbar, bei RT-systemen ist er sogar als Minimalzeit garantiert.

MfG,

von Fpgakuechle K. (Gast)


Lesenswert?

Karl H. schrieb:

> Im Wikipedia Artikel ist ein Link zu einem PDF mit einer etwas genaueren
> Beschreibung der Probleme. IMHO ist die Darstellung im Wiki etwas
> verzerrend verkürzt.

Offensichtlich, deshalb verweise ich auch immer das ich mich auf die 
WP-Darstellung beziehe. Konkret auf diese

"Diese war völlig ungenügend und funktionierte nur dann, wenn der 
Benutzer seine Eingaben relativ langsam machte. Aber nach einer gewissen 
Einarbeitungszeit konnten die Benutzer die Eingaben schneller tätigen, 
als der Therac-25 zwischen den verschiedenen Modi umgeschaltet werden 
kann"

https://de.wikipedia.org/w/index.php?title=Therac-25&oldid=96876886#Programmfehler


MfG,

von Karl H. (kbuchegg)


Lesenswert?

Fpga K. schrieb:
> Karl H. schrieb:
>
>> Im Wikipedia Artikel ist ein Link zu einem PDF mit einer etwas genaueren
>> Beschreibung der Probleme. IMHO ist die Darstellung im Wiki etwas
>> verzerrend verkürzt.
>
> Offensichtlich, deshalb verweise ich auch immer das ich mich auf die
> WP-Darstellung beziehe. Konkret auf diese

Schon klar.

Ich hab mich auch geirrt. Der Link stammt nicht aus dem Wiki
Es ist dieser hier
https://www.google.at/webhp?sourceid=chrome-instant&ion=1&espv=2&ie=UTF-8#q=Therac-25

der die ganze Sache ein wenig ausführlicher beleuchtet, als es ein WIki 
naturgemäss kann.

Aber wie sind wir eigentlich darauf gekommen?
Ach ja. Ausführungszeit.
Aus dem PDF geht hervor, dass die Ausführungszeit der Tastatureingabe 
nicht die eigentlich wesentliche Ursache war. Sie war Mittel zum Zweck, 
um das Problem hervorzurufen. Eigentliche Ursache waren ganz andere 
Fehler.

Ich geh übrigens nicht mit der Meinung des PDF-Autors konform, dass man 
den Bug leicht hätte beheben können, indem man das Flag an anderer 
Stelle hätte löschen sollen. Ich seh nämlich oft auch, dass schon die 
Verwendung von vielen Flags viele Softwareprobleme hervorrufen können. 
Irgendwann landet man dann in der Falle, dass man den Überblock 
verliert, wer wann warum und zu welchem Zeitpunkt welches Flag setzt und 
was das jetzt bedeutet. Flags sind manchmal notwendig, manchmal aber 
auch ein Zeichen für schlechtes Design.

: Bearbeitet durch User
von Fpgakuechle K. (Gast)


Lesenswert?

Karl H. schrieb:

> Wenn ich das schon lese: das Programm überwacht ob der Cursor rechts
> unten ist bzw. ob er das Ende einer Zeile erreicht hatte", dann läuft es
> mir kalt runter.

Ja das waren Zeiten, da hat die CPU bei Z80 Einplatinencomputer noch den 
Videospeicher als Parameterspeicher benutzt.

Aber bei dem system ist doch von einem VT100 terminal die Rede. Sendet 
das der Mainframe immer die aktuelle Cursorposition?

MfG

von Fpgakuechle K. (Gast)


Lesenswert?

Karl H. schrieb:

> Ich hab mich auch geirrt. Der Link stammt nicht aus dem Wiki
> Es ist dieser hier
> 
https://www.google.at/webhp?sourceid=chrome-instant&ion=1&espv=2&ie=UTF-8#q=Therac-25

Hm, der link wird zu einer google hitliste welchen link meinst du davon?

Diese Beschreibung:
http://sunnyday.mit.edu/papers/therac.pdf
scheint mir noch sehr detailiert.

Da wird erwähnt das das flag nicht ungleich 0 gesetzt wurde sondern 
inkrementiert. War wohl als error/update-count gedacht. Leider wird bei 
8 bit zahlen der (error-)count nach 256 inkrements wieder 0.

MfG

von Falk B. (falk)


Lesenswert?

@ Karl Heinz (kbuchegg) (Moderator)


>Wenn Werte eingestellt werden, der Benutzer danach auf 'Los' drückt und
>danach kann ich die Werte immer noch verändern, ohne dass ich zuerst mal
>die Maschine stoppen muss, dann ist da im Benutzerinterface was schief
>gelaufen.

Meine Rede!

>Benutzer noch Arbeitsparameter ändern kann. Eine Änderung bedeutet:
>Maschine stoppt und der ganze Prozess läuft von vorne weg erneut los.
>Inklusive Prüfung der kompletten Eingangswerte.
>Das ganze war eine Fehldesign.

GENAU!

>Unterricht zuständig waren. Wer "F ist m mal a" sagen konnte und wem das
>dann auch noch was sagte, wurde zum Informatiker befördert.

;-)

von Falk B. (falk)


Lesenswert?

@ Fpga Kuechle (fpgakuechle) Benutzerseite

>> Das halte ich für einen gefährlichen Irrtum!

>Wo steckt da der Irrtum?

>T1 <= Ta # Maximaldauer
>T2 >= Tb # Minimaldauer

>Ta < Tb  # Parameterbedingung

>-> T1 != T2

Denkst du, diese diffusen Zeilen erklären oder beweisen IRGENDETWAS?

>> Erstens darf kein Overflow auftreten, egal was passiert.
>? Nö Overflow ist ein propates Mittel um Fehlzustände zu erkennen,

OK.

>was nicht passieren darf ist das ein overflow der ein Fehlzuständ
>signalisiert ignoriert oder ohne korrekte Behandlung zurückgesetzt wird
>-> das scheint hier passiert zu sein.

Gut, das meinte ich dann wohl.

>> Und das hätte man auch mit Funktionen im konstanter
>> Laufzeit nicht verhindert!

>Hatt ja auch keiner behauptet.

Doch, das hattest du indirekt.

> Es ist ja nur darauf hingewiesen worden
>das eine Tastaturroutine mit konstanter Laufzeit das systemdesign
>dahingehend vereinfacht das keine zu schnelle Eingaben passieren können.

Auch das ist eine Aussage, der ich mich keine Sekunde anschließen kann.
Sie ist schlicht falsch. Wer schon mit einer Tastaturroutine mit 
nichtkonstanter Laufzeit in Bedrängnis gerät, hat ein grundlegendes 
Problem. Dass man prinzipiell die Komplexität eines Entwirfs möglichst 
gering halten sollte, eben um die Testbarkeit zu verbessern bzw. zu 
vereinfachen, ist davon unberührt.

>Halt wie der Tiefpass bei der AD Wandlung die Aliaseffekte verhindert.

Vollkommen unpassender Vergleich.

>Nimm es einfach als eine HAL die Unterschiede in der Tippgeschwindigkeit
>wegbügelt.

>UART ohne built-in-FIFO wie der 8250 wären auch ein Grund für eine
>Tastaturroutine mit konstanter Laufzeit.

Irgendwie bist du besessen von der Idee mit der konstanten Laufzeit, 
warum auch immer. Und wie Peter mehrfach feststellte, vermischt du hier 
Hardwarelogik und Glitches mit Software.

>Das man dennoch vor dem Schuß sicherstellen muß das die Kanone nicht auf
>tödliche Dosis gestellt ist, bleibt davon unberührt.

Aber eben DARUM geht es die ganze Zeit! Eine Funktion mit konstanter 
Laufzeit hilft hier in keinster Weise! Race Conditions werden damit 
nicht prinzipiell verhindert, schlimmstenfalls kaschiert, weil sie 
seltener auftreten! Wie Karl heinz schon sagte hätte man den Fehler mit 
ganz anderen Mitteln verhindern müssen (gescheites Softwaredesign).

von Fpgakuechle K. (Gast)


Lesenswert?

Falk B. schrieb:

> Denkst du, diese diffusen Zeilen erklären oder beweisen IRGENDETWAS?
>

> Vollkommen unpassender Vergleich.
>

> Irgendwie bist du besessen von der Idee mit der konstanten Laufzeit,
> warum auch immer.

> Und wie Peter mehrfach feststellte, vermischt du hier
> Hardwarelogik und Glitches mit Software.
>

> Aber eben DARUM geht es die ganze Zeit!


Diesem Stakkato an wohlformulierten Argumenten habe ich nichts entgegen 
zu setzen. Darum,

Over and Out

von Falk B. (falk)


Lesenswert?

We agree to disagree ;-)

von Bastler (Gast)


Lesenswert?

Sowas wie Therac-25 entsteht, wenn einer glaubt sein 
Synchronisierungsproblem käme von der unterschiedlichen Laufzeit 
verschiedener Softwarepfade. Manches muß nicht nur unter günstigen 
Umständen funktionieren, sondern nie versagen. (wobei es nie im Sinne 
von 0 sowenig existiert wie immer in Sinne von 100%)
Als bei einem Softwareunternehmen Arbeitender hab ich aber gelernt, daß 
nicht solide Arbeit zählt, sondern wie man sein Halbwissen verkauft. Zum 
Glück geht's bei uns nicht um Maschinen, die Menschen töten. Aber ich 
bin mir sicher, der Therac-3000 Bug ist schon irgendwo in Arbeit. Von 
einem, der sich so gut auskennt, daß er niemals an sich zweifeln würde.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Walter T. schrieb:
> Jetzt will ich dafür sorgen, daß diese Funktion immer möglichst
> gleich lange zur Ausführung benötigt, egal mit welchem Eingabewert
> sie aufgerufen wird.

Ohne genaueres über die Hardware oder den Rest der Software (IRQs etc.) 
zu wissen geht das nicht.

Falls die Ausführungszeiten einzelner Instruktionen statisch sind (keine 
Cache-Effekte etc.), dann kann man versuchen den Vergleich in Arithmetik 
aufzulösen, d.h. anstatt
 
1
if (a < b)
2
    c = d;
 
wird der neue Wert per Maskierung zugewiesen:
 
1
uint16_t delta = a < b;
2
delta = -(delta >> 15); // in -1, 0
3
c = (c & ~delta) | (d & delta);
 
Zusätzlich ist zu überprüfen, ob dies tatsächlich zu linearem Code 
übersetzt wird, und für GCC zusätzlich
1
__attribute__((__noinline__,__noclone__))

Falls vom Compiler unterstützt (z.B. von gcc) oder wenn sizeof(int) > 2 
geht auch
 
1
int16_t delta = a < b;
2
delta >>= 15; // in -1, 0
3
c = (c & ~delta) | (d & delta);

von Walter T. (nicolas)


Lesenswert?

Johann L. schrieb:
> d.h. anstatt
>  if (a < b)
>     c = d;
> wird der neue Wert per Maskierung zugewiesen:
>  uint16_t delta = a < b;
> delta = -(delta >> 15); // in -1, 0
> c = (c & ~delta) | (d & delta);

Jetzt kommen wir der Sache näher. Wo lernt man solche Bitspielereien?

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Hatte es doppelt gemoppelt... Korrekt ist so:
 
1
uint8_t mask = -(a < b);
2
c = (c & mask) | (d & ~mask);
 
oder so
 
1
uint8_t mask = (a - b) >> 15;
2
c = (c & mask) | (d & ~delta);
 
und mit avr-gcc z.B. so:
1
#include <stdint.h>
2
3
// a < b ? -1 : 0
4
static inline uint8_t m1 (uint8_t a, uint8_t b)
5
{
6
    __asm ("cp %1, %2 $ sbc %0, %0" : "=r" (a) : "r" (a), "r" (b));
7
    return a;
8
}
9
10
// a < b ? c : d;
11
uint8_t select (uint8_t a, uint8_t b, uint8_t c, uint8_t d)
12
{
13
    uint8_t mask = m1 (a, b);
14
    return (c & mask) | (d & ~mask);
15
}
 
1
select:
2
  cp r24, r22 $ sbc r24, r24
3
  mov r25,r24
4
  com r25
5
  and r18,r25
6
  and r24,r20
7
  or r24,r18
8
  ret

: Bearbeitet durch User
von Robert L. (lrlr)


Lesenswert?

>Gibt es unter C überhaupt eine sinnvolle Möglichkeit, eine Funktion
>konstanter Laufzeit zu implementieren und wenn ja: wie?

(es stellt sich hier zwar die Frage ob das überhaupt notwendig ist, 
aber:)
nein, gibt es nicht

maximal wenn man sich auf EINE CPU einschränkt
den Compiler NIE updated,
ein paar "Randbedingungen" wie: SingleThread, keine IRQs, 
funktionierender Quarz, KEIN first/second/usw level cache usw.

also maximal auf eine URALT CPU die seit 20 Jahren 1:1 identisch gebaut 
wird..

von Tom (Gast)


Lesenswert?

Fpga K. schrieb:
> Es ist ja nur darauf hingewiesen worden
> das eine Tastaturroutine mit konstanter Laufzeit das systemdesign
> dahingehend vereinfacht das keine zu schnelle Eingaben passieren können.

Das löst das Problem auf der völlig falschen Ebene.
Was man erreichen will, hat man explizit hinzuschreiben. Nebenwirkungen 
(wie die Laufzeit) auszunutzen ist die schlimmste Art zu programmieren, 
die ich kenne.


Apfelnudelsalat Rezept 1:
1kg Nudeln 10min lang kochen, dann abgießen. 20 Äpfel schälen und 
kleinschneiden. Zusammenrühren.

Apfelnudelsalat Rezept 2:
1kg Nudeln zum kochen bringen. Dann 20 Äpfel schälen und kleinschneiden. 
Ist man mit dem Kleinschneiden fertig, die Nudeln abgießen. 
Zusammenrühren.

Ich verwende Rezept 2, weil ich Rezept 1 doof finde. Jetzt muss ich die 
Schärfe und Länge meines Schälmessers anpassen, damit die Nudeln nicht 
vermatschen. Wie geht das?

von Josef G. (bome) Benutzerseite


Lesenswert?

Für Leute, die sich für Programme mit berechenbarer Ausführungszeit
interessieren: Schaut euch mal mein Projekt an.
Beitrag "Re: Gibt es eine Programmiersprache mit diesem Schleifentyp?"
http://www.bomerenzprojekt.de

von Karl H. (kbuchegg)


Lesenswert?

Josef G. schrieb:
> Für Leute, die sich für Programme mit berechenbarer Ausführungszeit
> interessieren

Auch ohne 'Schleife mit Hineinsprung' ist die Ausführungszeit 
berechnbar, das ist nicht das Problem.
Und wenn sie nicht berechnbar ist (weil im Vorfeld keiner weiss, 
wieviele Schleifeniterationen gemacht werden), dann ist sie das bei 
deiner 'Schleife mit Hineinsprung' auch nicht.

Du löst schon wieder mal ein Problem, das in Wirklichkeit keines ist.

von Falk B. (falk)


Lesenswert?

@ Karl Heinz (kbuchegg) (Moderator)

>Du löst schon wieder mal ein Problem, das in Wirklichkeit keines ist.

Und du redest schon wieder mit einem lernresistenten Menschen.

von Josef G. (bome) Benutzerseite


Lesenswert?

Karl H. schrieb:
> Und wenn sie nicht berechnbar ist (weil im Vorfeld keiner weiss,
> wieviele Schleifeniterationen gemacht werden)

Man zählt die Iterationen und führt danach
(Maxzahl-Istzahl) leere (Delay-) Schleifen aus.

von Michael B. (laberkopp)


Lesenswert?

Walter T. schrieb:
> ich habe eine relativ einfache Funktion, die einen analogen Wert
> klassiert:// Mittelwerte der einzelnen Tastendruecke abfallend sortiert
> #define N_KEYVALUE 6
> static const uint8_t keyvalue[N_KEYVALUE] = {255,159,101,63,24,0};
>
> /* Gedrueckte Taste an Widerstandsreihe erkennen
>  * aval: Analoger Wert
>  * return: Klassierter Wert */
> uint8_t decodeAnalogKey(uint8_t aval)
> {
>   uint8_t thresh, i, dummy;
>   uint8_t keystate = N_KEYVALUE-1;
>
>   // Die Schleife wird immer gleichoft durchlaufen, egal welche Taste
>   // gedrueckt ist
>   for(i = 0; i<N_KEYVALUE-1; i++) {
>
>     // Division durch 2 vor der Addition erhoeht Ungenauigkeit auf
>     // 2, bleibt aber immer im Wertebereich uint8_t.
>     thresh = keyvalue[i]/2+keyvalue[i+1]/2;
>
>     if(aval < thresh ) {
>       // Diese Zuweisung erfolgt unterschiedlich oft, je nachdem
>       // welche Taste gedrueckt ist
>       keystate = i;
>     }
>     else {
>                     // Wird wegoptimiert, zeigt aber, was ich meine
>       dummy = i;
>     }
>   }
>
>   return keystate;
> }
>
> Jetzt will ich dafür sorgen, daß diese Funktion immer möglichst gleich
> lange zur Ausführung benötigt

Man kann die ganze Berechnung
keyvalue[i]/2+keyvalue[i+1]/2;
vorab machen
static const uint8_t thresh[N_KEYVALUE] = {207,130,82,44,12};
Dann kannst du prüfen ohne if
   for(i = 0; i<N_KEYVALUE-1; i++) {
     keystate = (keystate<<1) | (aval < thresh[i]) ;
Es kommt halt nicht 0..5 raus, sondern 0, 1, 3, 7, 15
Wenn das stört, kann man nochmal umsetzen
translate[]={0,1,0,2,0,0,0,4,0,0,0,0,0,0,0,15};
keystate = translate[keystate]

Und damit hat man konstante Laufzeit.

von Bastler (Gast)


Lesenswert?

Wenn man verschiedene (und verschieden lang laufende) Pfade in einem 
Programm hat und man muß egal welcher Pfad zu einem exakten Zeitpunkt 
seit dem Start fertig sein, dann muß man eben am Ende auf diese 
Maximallaufzeit synchronisieren. Also am Anfang Timer starten und am 
Ende darauf warten. Dabei darf wegen nichtexistenter negativer Wartezeit 
kein Code-Pfad länger laufen, als diese "Wunschzeit". Das Gänze per 
Taktzyklenzählen lösen zu wollen, ist ein großer Aufwand, von vielen 
Randbedingungen abhängig, die man nicht im Griff hat (Int's, die 
ungeplant dazwischen funken, überigens auch bei delay_us()/delay_ms() 
und lohnt sich allenfalls bei sowas wie "USB per Firmware".

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.