Auf die Frage der Geschwindigkeit und Messung in Arduino



    Dieses Problem ergab sich aus der Untersuchung der Geschwindigkeit der Arbeit von Arduino bei der Ausführung verschiedener Befehle (mehr dazu in einem separaten Beitrag). Im Verlauf der Studie wurden Zweifel an der Konstanz der Operationszeit einzelner Teams geäußert, als sich der Wert der Operanden (wie sich herausstellte, als nicht unvernünftig) änderte, und es wurde beschlossen, die Ausführungszeit eines bestimmten Befehls zu schätzen. Dafür wurde ein kleines Programm geschrieben (von dem gesagt wurde, die Skizze solle die Klasse verlassen), was auf den ersten Blick die Hypothese bestätigte. In der Ausgabe können Sie die Werte von 16 und 20 beobachten, aber manchmal gibt es 28 und sogar 32 Mikrosekunden. Wenn Sie die Daten mit 16 (Taktfrequenz MK) multiplizieren, erhalten Sie die Ausführungszeit in Takttakten MK (von 256 bis 512). Leider ist der wiederholte Durchlauf des Hauptprogrammzyklus (mit den gleichen Anfangsdaten) unter Beibehaltung des Gesamtbildes ergibt eine unterschiedliche Verteilung der Ausführungszeit, so dass die tatsächlich stattfindenden Zeitvariationen nicht auf die Originaldaten bezogen sind. Die ursprüngliche Hypothese wurde widerlegt, aber sie wird interessant und mit was genau ist eine solche signifikante Streuung verbunden.

    Der notwendige Hinweis - Ich verstehe vollkommen, dass zur Messung der Ausführungszeit von Befehlen komplexere Programme verwendet werden sollten. Für eine grobe Schätzung ist dies jedoch ausreichend und wird später demonstriert.

    Die Zeit verändert sich also sehr stark und sucht nach den Ursachen dieses Phänomens. Zuerst achten wir auf die Vielzahl der erhaltenen Werte, schauen uns die Beschreibung der Arbeit mit der Zeitbibliothek an und stellen fest, dass 4 Mikrosekunden ein Messquant sind. Daher ist es besser, zu den Quanten zu gehen und zu verstehen, dass wir 4 oder 5 (ziemlich oft) und 6 oder 7 oder 8 (sehr selten) Maßeinheiten. Mit der ersten Hälfte ist alles einfach - liegt der gemessene Wert zwischen 4 und 5 Einheiten, wird der Spread unvermeidlich. Wenn wir jedoch die Anzahl als unabhängig betrachten, können wir die Messgenauigkeit durch statistische Methoden erhöhen, was akzeptable Ergebnisse liefert.

    Mit der zweiten Hälfte (6,7,8) ist es jedoch schlimmer. Wir haben festgestellt, dass der Spread nicht mit den Quelldaten korreliert, was bedeutet, dass dies eine Manifestation anderer Prozesse ist, die die Ausführungszeit von Befehlen beeinflussen. Beachten Sie, dass Emissionen recht selten sind und für den berechneten Durchschnittswert keine Bedeutung haben. Man könnte sie völlig ignorieren, aber das ist nicht unser Stil. Im Laufe der Jahre im Ingenieurwesen wurde mir klar, dass es unmöglich war, neponyatki zu verlassen, egal wie unbedeutend sie schienen, weil sie die widerliche Eigenschaft haben, im ungünstigsten Moment auf den Rücken zu schlagen (gut oder sogar zu greifen).

    Beginnen Sie mit einer Hypothese 1- das bequemste (was die Bequemlichkeit und Vielseitigkeit angeht, ist nach dem direkten Eingriff des Erstellers nachrangig) - Software-Pannen natürlich nicht meine, meine Programme niemals fehlerhaft, sondern Plug-In-Bibliotheken (Compiler, Betriebssystem, Browser usw.) ). Da ich das Programm im Emulator auf www.tinkercad.com ausführen kann , können wir außerdem auf die Emulator-Fehler verweisen und das Thema schließen, da der Quellcode für uns nicht verfügbar ist. Nachteile dieser Hypothese:

    1. Von Zyklus zu Zyklus variiert der Ort der Abweichungen, was darauf hinweist.
    2. Diese Site unterstützt weiterhin AutoDesk, obwohl das Argument schwach ist.
    3. "Wir haben das Postulat akzeptiert, dass das, was passiert, keine Halluzination ist, sonst wäre es einfach nicht interessant."

    Die folgende Annahme ist die Auswirkung einiger Hintergrundprozesse auf das Messergebnis. Es scheint nichts zu tun, außer wir glauben, obwohl ... wir die Ergebnisse in Serial anzeigen. Es gibt eine Hypothese 2 - die Zeit des Rückzugs wird manchmal (seltsam so ... aber alles passiert) zur Ausführungszeit des Befehls hinzugefügt. Es ist zwar zweifelhaft, wie viel von dieser Ausgabe vorhanden ist, aber trotzdem - wir fügen Flush hinzu und es hat nicht geholfen, wir fügen dem Ergebnis der Ausgabe eine Verzögerung hinzu, und es hat nicht geholfen. Wir bringen die Ausgabe normalerweise aus dem Zyklus heraus - immer noch Zeitsprünge - dies ist definitiv keine serielle.

    Okay, was bleibt, ist die Organisation des Zyklus selbst (mit welcher Angst es ist, seine Dauer zu ändern, ist nicht klar) und das ist alles ... obwohl micros () noch übrig ist. Ich meinte, dass die Ausführungszeit des ersten Aufrufs dieser Funktion und des zweiten die gleiche ist, wenn diese beiden Werte abgezogen werden. Ich bekomme Null, aber wenn diese Annahme falsch ist?

    Hypothese 3 - manchmal wird die zweite Zeitreferenz länger ausgeführt als die erste, oder die mit der Zeitmessung zusammenhängenden Aktionen beeinflussen manchmal das Ergebnis. Wir betrachten den Quellcode der Funktion des Arbeitens mit der Zeit (arduino-1.8.4 \ hardware \ arduino \ avr \ cores \ arduino \ wiring.c - Ich habe wiederholt meine Einstellung zu solchen Dingen geäußert, ich werde mich nicht wiederholen) und sehe, dass 1 mal von 256 Hardware-Inkrementierungszyklen des unteren Teils des Zählers werden unterbrochen, um den höheren Teil des Zählers zu erhöhen.

    Unsere Zyklusausführungszeit reicht von 4 bis 5, also können Sie 170 * (4..5) / 256 = von drei bis vier anomalen Werten auf einem Segment von 170 Messungen erwarten. Wir sehen aus - sehr ähnlich, es gibt wirklich 4 von ihnen. Um den ersten und den zweiten Grund zu trennen, führen wir die Berechnungen mit einem kritischen Abschnitt mit verbotenen Interrupts durch. Das Ergebnis ändert sich nicht viel, Ausreißer treten immer noch auf, was bedeutet, dass zusätzliche Zeit einen Aufruf an micros () auslöst. Hier können wir nichts tun, obwohl der Quellcode verfügbar ist, aber wir können ihn nicht ändern - Bibliotheken sind in Binärdateien enthalten. Natürlich können wir unsere eigenen Funktionen für das Arbeiten mit der Zeit schreiben und ihr Verhalten beobachten, aber es gibt einen einfacheren Weg.

    Wenn der mögliche Grund für die Verlängerung der Dauer die "lange" Interruptverarbeitung ist, schließen wir die Möglichkeit ihres Auftretens im Messprozess aus. Warten Sie dazu auf seine Manifestation und führen Sie erst dann einen Messzyklus durch. Da ein Interrupt viel seltener auftritt als unser Messzyklus, kann dessen Abwesenheit garantiert werden. Wir schreiben das entsprechende Programmfragment (unter Verwendung von aus dem Quellcode extrahierten Dirty-Hacker- Informationen) und "dies ist eine 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 in 166 Zyklen. entspricht dem zuvor gemessenen Wert. Die Hypothese kann als bestätigt betrachtet werden.

    Eine weitere Frage bleibt - was in Unterbrechungen so lange gemacht wird, dass es dauert
    (7.8) - (5) ~ 2 Quantum = * 4 = 8 μs * 16 = 128 Prozessorzyklen? Wir wenden uns dem Quellcode zu (dh dem vom Compiler auf godbolt.com generierten Assembler-Code) und stellen fest, dass der Interrupt selbst etwa 70 Zyklen ausführt, von denen 60 permanent sind, und beim Lesen zusätzliche Kosten von 10 Zyklen entstehen, die beim Anschlagen insgesamt 70 Zyklen verursachen Unterbrechung - weniger als erhalten, aber nahe genug. Der Unterschied wird dem Unterschied zwischen Compilern oder Verwendungsmodi zugeschrieben.

    Nun, jetzt können wir die tatsächliche Ausführungszeit des PT-Additionsbefehls mit verschiedenen Argumenten messen und sicherstellen, dass sie sich wirklich ändert, wenn sich die Argumente ändern: von 136 Zyklen für 0,03 bis 190 für 0,63 (magische Zahl) und nur 162 für 10,63. Mit einer Wahrscheinlichkeit von 99,9% ist dies auf die Notwendigkeit einer Angleichung und die Besonderheiten seiner Implementierung in dieser speziellen Bibliothek zurückzuführen. Diese Studie geht jedoch eindeutig über die Grenzen des betrachteten Problems hinaus.

    Die Anwendung ist der Text des Programms:
    voidsetup(){
      Serial.begin(9600);
    }
    volatilefloat t; 	// так надоvoidloop(){
    int d[170];
    unsignedlong 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();	// засекаем время/*
        cli();	// тут был вход в критическую секцию - не помогло
    */
        t=10.63;	// начальное значение для операции
        t=t+dt;		// измеряемая операция/*
        sei();	// завершение критической секции
    */
        time = micros();	// время окончания
        time1=time-time1;
        d[i]=time1/4;
    /*
    Serial.print(time1);   // вот тут результаты и прыгали
    Serial.flush(); 	     // не помогло убрать выбросы
    Delay(20);		     // тоже не помогло
    */
      };
    // выводим запомненные результаты, считаем и выводим среднее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();	// здесь ставим точку останова, чтобы посмотреть графики вывода
    }
    


    Jetzt auch beliebt: