
Dieses Problem trat bei der Untersuchung der Leistung von Arduino bei der Ausführung verschiedener Befehle auf (mehr dazu in einem separaten Beitrag). Im Verlauf der Studie traten Zweifel an der Konstanz der Arbeitszeit einzelner Befehle auf, als sich der Wert der Operanden änderte (wie sich später herausstellte, nicht unangemessen) und beschlossen wurde, die Ausführungszeit eines separaten Befehls abzuschätzen. Zu diesem Zweck wurde ein kleines Programm geschrieben (das sagte, die Skizze soll die Klasse verlassen), das auf den ersten Blick die Hypothese bestätigte. Abschließend können Sie die Werte 16 und 20 beobachten, aber manchmal werden 28 und sogar 32 Mikrosekunden gefunden. Wenn wir die empfangenen Daten mit 16 (MK-Taktfrequenz) multiplizieren, erhalten wir die Ausführungszeit in MK-Zyklen (von 256 bis 512). Leider führt ein wiederholter Durchlauf des Hauptprogrammzyklus (mit denselben Anfangsdaten) unter Beibehaltung des Gesamtbildes bereits zu einer unterschiedlichen Verteilung der Ausführungszeit, sodass die tatsächlichen Zeitschwankungen nicht mit den Anfangsdaten zusammenhängen. Die ursprüngliche Hypothese wird widerlegt, aber sie wird interessant und was genau mit einer solch signifikanten Streuung verbunden ist.
Notwendiger Hinweis - Ich verstehe sehr gut, dass komplexere Programme verwendet werden sollten, um die Ausführungszeit von Befehlen zu messen. Für eine grobe Beurteilung ist jedoch eine, die später demonstriert wird, völlig ausreichend.
Die Zeit ändert sich also und wir suchen nach den Ursachen für dieses Phänomen. Zunächst achten wir auf die Vielzahl der erhaltenen Werte, sehen uns die Beschreibung der Arbeitsbibliothek im Zeitverlauf an und stellen fest, dass 4 µs ein Messquantum ist. Es ist daher besser, zu Quanten zu gehen und zu verstehen, dass wir 4 oder 5 (sehr oft) und 6 oder 7 oder erhalten 8 (sehr seltene) Einheiten. Mit der ersten Hälfte ist alles einfach - wenn der gemessene Wert zwischen 4 und 5 Einheiten liegt, wird die Streuung unvermeidlich. Wenn wir die Proben als unabhängig betrachten, können wir außerdem die Messgenauigkeit durch statistische Methoden erhöhen, was wir tun, um akzeptable Ergebnisse zu erzielen.
Aber mit der zweiten Hälfte (6,7,8) ist es schlimmer. Wir haben festgestellt, dass die Streuung nicht mit den Quelldaten korreliert, was bedeutet, dass dies eine Manifestation anderer Prozesse ist, die die Ausführungszeit von Befehlen beeinflussen. Es ist zu beachten, dass Emissionen eher selten sind und keinen signifikanten Einfluss auf den berechneten Durchschnittswert haben. Es wäre möglich, sie überhaupt zu vernachlässigen, aber das ist nicht unser Stil. Im Allgemeinen habe ich im Laufe der jahrelangen Arbeit im Ingenieurwesen festgestellt, dass Sie keine Missverständnisse hinterlassen können, egal wie unbedeutend sie erscheinen, da sie eine widerliche Fähigkeit haben, im ungünstigsten Moment in den Rücken zu schlagen (na ja, oder wo sie sonst hin gelangen).
Wir fangen an,
Hypothese 1 aufzustellen - die bequemste (in Bezug auf Bequemlichkeit und Vielseitigkeit ist sie nach dem direkten Eingreifen des Erstellers an zweiter Stelle) - Softwareprobleme natürlich nicht meine, meine Programme scheitern nie, sondern verbundene Bibliotheken (Compiler, Betriebssystem, Browser usw.). - das Notwendige ersetzen). Da ich das Programm im Emulator unter
www.tinkercad.com ausführe , können Sie außerdem auf die Emulatorfehler verweisen und das Thema schließen, da uns die Quellen nicht zur Verfügung stehen. Nachteile dieser Hypothese:
- Von Zyklus zu Zyklus ändert sich der Ort der Abweichungen, was darauf hindeutet.
- Diese Seite unterstützt weiterhin AutoDesk, obwohl das Argument eher schwach ist.
- "Wir haben das Postulat akzeptiert, dass das, was passiert, keine Halluzination ist, sonst wäre es einfach uninteressant."
Die nächste Annahme ist der Einfluss einiger Hintergrundprozesse auf das Messergebnis. Wir scheinen nichts anderes zu tun, als zu glauben, aber ... wir geben die Ergebnisse in seriell aus.
Hypothese 2 entsteht - die Ausgabezeit wird manchmal (so seltsam ... aber es passiert) zur Ausführungszeit des Befehls hinzugefügt. Es ist zwar zweifelhaft, wie viel diese Ausgabe vorhanden ist, aber dennoch - das Hinzufügen von Flush hat nicht geholfen, das Hinzufügen einer Verzögerung zum Vervollständigen der Ausgabe hat nicht geholfen und im Allgemeinen die Ausgabe aus der Schleife verschoben - ohnehin Zeitsprünge - dies ist definitiv nicht seriell.
Nun, was bleibt, ist die Organisation des Zyklus selbst (von dem ein Schrecken ist, seine Dauer zu ändern, es ist nicht klar) und das ist alles ... obwohl micros () geblieben ist. Ich meinte, dass die Ausführungszeit des ersten Aufrufs dieser Funktion und des zweiten gleich ist und wenn ich diese beiden Werte subtrahiere, bekomme ich Null, aber wenn diese Annahme falsch ist?
Hypothese 3 - Manchmal dauert der zweite Aufruf der Zeitzählung länger als der erste, oder die mit der Zeitzählung verbundenen Aktionen wirken sich manchmal auf das Ergebnis aus. Wir schauen uns den Quellcode der Funktion des Arbeitens mit der Zeit an (arduino-1.8.4 \ hardware \ arduino \ avr \ cores \ arduino \ wiring.c - Ich habe wiederholt meine Einstellung zu solchen Dingen zum Ausdruck gebracht, ich werde mich nicht wiederholen) und wir sehen, dass 1 von 256 Mal Zyklen der Hardwareerhöhung des jüngeren Teils des Zählers werden unterbrochen, um den älteren Teil des Zählers zu erhöhen.
Unsere Zyklusausführungszeit beträgt 4 bis 5, sodass wir 170 * (4..5) / 256 = drei bis vier anomale Werte in einem Segment von 170 Messungen erwarten können. Wir sehen aus - es sieht sehr ähnlich aus, es gibt wirklich 4 davon. Um den ersten und den zweiten Grund zu trennen, führen wir Berechnungen nach dem kritischen Abschnitt mit verbotenen Unterbrechungen durch. Das Ergebnis ändert sich nicht viel, Emissionen haben immer noch einen Platz, was bedeutet, dass Mikros zusätzliche Zeit einbringen (). Hier können wir nichts tun, obwohl der Quellcode verfügbar ist, können wir ihn nicht ändern - Bibliotheken sind in den Binärdateien enthalten. Natürlich können wir unsere eigenen Funktionen für die Arbeit mit der Zeit schreiben und ihr Verhalten beobachten, aber es gibt einen einfacheren Weg.
Da die „lange“ Verarbeitung des Interrupts ein möglicher Grund für die Verlängerung der Dauer ist, schließen wir die Möglichkeit seines Auftretens während des Messvorgangs aus. Warten Sie dazu auf seine Manifestation und führen Sie erst dann einen Messzyklus durch. Da die Unterbrechung viel seltener auftritt als unser Messzyklus dauert, können wir ihre Abwesenheit garantieren. Wir schreiben das entsprechende Fragment des Programms (unter Verwendung von
Dirty Hacks mit Informationen, die aus dem Quellcode extrahiert wurden) und "das ist solche Straßenmagie", alles wird normal - wir messen die Ausführungszeit von 4 und 5 Quanten mit einem Durchschnittswert der Ausführungszeit der Additionsoperation mit PT von 166 Taktzyklen, die entspricht dem zuvor gemessenen Wert. Die Hypothese kann als bestätigt angesehen werden.
Eine weitere Frage bleibt offen - und was dauert bei Unterbrechungen so lange, was dauert es?
(7.8) - (5) ~ 2 Quanten = * 4 = 8 ms * 16 = 128 Prozessorzyklen? Wir wenden uns dem Quellcode zu (dh dem Assembler-Code, der vom Compiler auf godbolt.com generiert wurde) und sehen, dass der Interrupt selbst ungefähr 70 Zyklen ausgeführt wird, 60 davon konstant, und beim Lesen entstehen zusätzliche Kosten von 10 Zyklen, insgesamt 70, wenn er aktiviert wird Unterbrechung - weniger als empfangen, aber nah genug. Wir führen den Unterschied auf den Unterschied zwischen Compilern oder Verwendungsmodi zurück.
Nun können wir die tatsächliche Ausführungszeit des PT-Additionsbefehls mit verschiedenen Argumenten messen und sicherstellen, dass sich beim Ändern der Argumente wirklich viel ändert: von 136 Takten für 0,0 bis 190 für 0,63 (magische Zahl), und dies ist nur 162 für 10,63. Mit einer Wahrscheinlichkeit von 99,9% ist dies auf die Notwendigkeit der Ausrichtung und die Merkmale ihrer Implementierung in dieser speziellen Bibliothek zurückzuführen, aber diese Studie geht eindeutig über den Rahmen des betrachteten Problems hinaus.
Anhang - Programmtext:void setup() { Serial.begin(9600); } volatile float t; // void loop() { int d[170]; unsigned long time,time1; float dt=1/170.; for (int i=0; i<170; ++i) { { // time1=micros(); long time2; do { time2=micros(); } while ((time2 & ~0xFF) == (time1 & ~0xFF)); }; time1=micros(); // t=10.63; // t=t+dt; // time = micros(); // time1=time-time1; d[i]=time1/4; }; // , float sum=0; for (int i=0; i<170; ++i) { sum+=d[i]; Serial.println(d[i]); }; Serial.println((sum/170-2.11)*4*16); //2.11 – Serial.flush(); // , }