Zuviel Werbung? - > Hier kostenlos beim SPS-Forum registrieren

Seite 1 von 3 123 LetzteLetzte
Ergebnis 1 bis 10 von 21

Thema: häufige Zeitberechnung, hohe CPU Auslastung

  1. #1
    Registriert seit
    17.02.2016
    Beiträge
    24
    Danke
    1
    Erhielt 2 Danke für 2 Beiträge

    Blinzeln


    Zuviel Werbung?
    -> Hier kostenlos registrieren
    Hallo,

    ich benötige einmal eure Hilfe bezüglich der Performance eines CP6606. (Beckhoff)
    im Bezug auf eine Zeitmessung.


    Situation:
    an dem CP hängt eine EK1101 klemme welche über EtherCat angesprochen wird.
    An dieser Klemme hängen wiederum Zählerkarten des Typs EL1512 (6 Karten)

    Programm:
    - Einlesen der Eingänge
    - CASE
    0: - speichere Time1: Systemzeit (GETSYSTEMTIME()) CASE=>1
    1: - Berechne: Zählerdifferenz := (aktuellen Zählerstand) - (alten Zählerstand), Ist dieser Größer als 100, dann
    - Speichere Time2 (Systemzeit) bereche differenz der beiden Zeiten, Time2-Time1,
    - weitere Berechnungen mit der Zeit und des Zählers
    - CASE=>2

    Dieser Funktionsblock zur Berechnung der Zeiten wird am Anfang des Programms in ein Array initialisiert, sodass es für jeden Messkanal zur verfügung steht.
    also für jeden Messkanal eine eigene Instanz.

    Nun wird in einer FOR-schleife die einzelnen Funktionsblöcke abgearbeitet uind die Werte in eine Struktur geschrieben.

    An sich funktioniert das ganze auch, bis auf diese Kleinigkeit.


    timing.jpg

    Bis die einzelnen Zähler ca. den Wert 100 erreicht haben, vergehen ca. 5 Sekunden.
    Der CPU leerlauf iegt bei ca. 15%.
    Wenn nun die Berechnung erfolgt, liegt dieser bei 60%
    Kann man an der Performance noch etwas verbessern?
    Ziel ist es bis zu 100 solcher Karten abzufragen und zu berechnen.



    MAIN
    VAR
    fbCalc: ARRAY [0..11] OF FB_Calc;
    END_VAR

    FOR idx := 0 TO 11 BY 1 DO
    fbCalc[idx]()
    END_FOR
    FB_CALC
    VAR
    ActSystemTime1 : T_ULARGE_INTEGER ;
    ActSystemTime2 : T_ULARGE_INTEGER ;
    UInt64TimeDif : T_ULARGE_INTEGER ;
    TimeDifSec : LREAL;
    fbReadSystemTime: GETSYSTEMTIME;
    END_VAR


    CASE iState OF
    0: // setze Zeit
    fbReadSystemTime(timeLoDW=> ActSystemTime1.dwLowPart, timeHiDW=> ActSystemTime1.dwHighPart);
    iMeas_PulsAlt := iPlsInputVal; // Speicher aktuellen Zählerstand
    iState := 1;

    1: //Lese Zählerstand bis differenz über 100 ist

    iDifPuls := iPlsInputVal - iMeas_PulsAlt;
    IF iDifPuls >= 100 THEN
    fbReadSystemTime(timeLoDW=> ActSystemTime2.dwLowPart,timeHiDW=> ActSystemTime2.dwHighPart); // Speicher aktuelle Zeit in zweiten Speicher
    UInt64TimeDif:= UInt64Sub64(ActSystemTime2, ActSystemTime1); // Berechne die Differenz
    TimeDifSec:= UINT64_TO_LREAL(UInt64TimeDif) / LREAL#10000000; // errechne sekundenwerte bsp. 10.12

    rMeas_Act := ( (UDINT_TO_REAL( iDifPuls) * LREAL_TO_REAL(TimeDifSec) ) / 100) ; // Berechnungen
    // ....

    iState :=0;
    END_IF

    END_CASE


    Vielen Dank im Voraus
    Zitieren Zitieren häufige Zeitberechnung, hohe CPU Auslastung  

  2. #2
    Registriert seit
    29.06.2015
    Beiträge
    33
    Danke
    2
    Erhielt 7 Danke für 6 Beiträge

    Standard

    Hallo Minehunter,

    du braucht die Systemzeit doch nur einmal pro Zyklus. Dazu musst du sie ja nicht in jedem Baustein neu aufrufen. Lese sie einmal zu Zyklusbeginn aus und übergebe das Ergebnis den ganzen FB's. Das sollte einiges an Zeit sparen.

    Grüße

  3. #3
    Minehunter ist offline Neuer Benutzer
    Themenstarter
    Registriert seit
    17.02.2016
    Beiträge
    24
    Danke
    1
    Erhielt 2 Danke für 2 Beiträge

    Standard

    Velen Dank für den Tipp,

    ich wahr der Ansicht, dass ich dies lieber alles in einem FB machen sollte, um nicht abhängig von äußeren Zeiten zu sein.

    So habe nun ein meinem FB die Start-Zeit übergeben.
    Diese wird nun im CASE 0: zwischengespeichert. dann springe ich gleich in CASE 1,
    Dort wird nun gewartet bis der Zähler auf 100 gestiegen ist, die Systemzeit wird jedesmal aufs neuem dem FB übergeben, sodass innerhlab des FB keine Zeiten mehr ausgelesen werden müssen.
    Ist der Zähler nun 100 wird einfach die gespeicherte Zeit aus CASE 0 mit der aktuellen Zeit vergleichen

    timimg2.PNG


    so richtig ist da aber eine verbesserung nicht zu erkennen.
    Das auslesen der aktuellen Zeit erfolgt nun nach jedem Zyklus.
    Nicht aber dann, wenn die Zähler den Stand von 100 erreicht haben. also ein definiertes auslesen

    Das system muss also ständig die Zeit auslesen, obwöhl es eventuell 5 sekunden zeit gehabt hätte.



    Vorteil der Neuen Variante:
    - Da die Systemzeit noch außerhalb der FOR-Schleife vom Systemabgefragt wird, kann diese innerhlab der FOR-Schleife an alle Funktionsblöcke übergeben werden.
    - das erspart aktuell 12mal den Zeit-Aufruf.

    Habt ihr weitere Ideen?

    VAR_INPUT
    ActSystemTime : T_ULARGE_INTEGER ; // Übergabe der Systemzeit, welche vor dem Funktionsblock, außerhlab der FORschleife einmal gelesen wird
    VAR_END


    CASE iState OF
    0: // setze Zeit
    //fbReadSystemTime(timeLoDW=> ActSystemTime1.dwLowPart, timeHiDW=> ActSystemTime1.dwHighPart);

    // NEU
    ActSystemTime1 := ActSystemTime;
    ...

    1: //Lese Zählerstand bis differenz über 100 ist

    iDifPuls := iPlsInputVal - iMeas_PulsAlt;
    IF iDifPuls >= 100 THEN
    //fbReadSystemTime(timeLoDW=> ActSystemTime2.dwLowPart,timeHiDW=> ActSystemTime2.dwHighPart); // Speicher aktuelle Zeit in zweiten Speicher
    //UInt64TimeDif:= UInt64Sub64(ActSystemTime2, ActSystemTime1);

    // NEU
    UInt64TimeDif:= UInt64Sub64(ActSystemTime, ActSystemTime1);
    TimeDifSec:= UINT64_TO_LREAL(UInt64TimeDif) / LREAL#10000000;
    .....
    iState :=0;
    END_IF

    Geändert von Minehunter (01.03.2016 um 14:34 Uhr)

  4. #4
    Registriert seit
    29.06.2015
    Beiträge
    33
    Danke
    2
    Erhielt 7 Danke für 6 Beiträge

    Standard

    Dann vielleicht:
    Alle FB's abfragen ob sie eine Zeit benötigen. Wenn ja Zeit auslesen und Zeit noch im selben zyklus noch einmal den FB's übergeben.
    Dann rufst du die Zeit wirklich nur dann auf wenn du sie brauchst und maximal einmal pro zyklus.

    Ansonsten kann ich deine Laufzeit nicht testen. Ich habe hier nur eine Lenze PLC mit 1,6 Ghz. da dauert das Zeit auslesen ca 10 µs.

  5. #5
    Registriert seit
    04.11.2014
    Beiträge
    139
    Danke
    1
    Erhielt 25 Danke für 23 Beiträge

    Standard

    Hallo,

    ich würde das CASE ganz weg lassen. Im Main rufst du einmal die Zeit ab. Dann rufst du die FB's auf und machst deine Berechnung. Das CASE kostet nur Zeit.

    Grüße

  6. #6
    Registriert seit
    22.06.2009
    Ort
    Sassnitz
    Beiträge
    11.184
    Danke
    923
    Erhielt 3.290 Danke für 2.659 Beiträge

    Standard

    Zitat Zitat von Minehunter Beitrag anzeigen
    An sich funktioniert das ganze auch, bis auf diese Kleinigkeit.


    timing.jpg

    Bis die einzelnen Zähler ca. den Wert 100 erreicht haben, vergehen ca. 5 Sekunden.
    Der CPU leerlauf iegt bei ca. 15%.
    Wenn nun die Berechnung erfolgt, liegt dieser bei 60%
    Kann man an der Performance noch etwas verbessern?
    Ziel ist es bis zu 100 solcher Karten abzufragen und zu berechnen.
    Was stört Dich eigentlich?
    Wenn Du gleichmäßig ausgelastete Zyklen willst, dann mach doch die Berechnungen immer.

    Wenn Du mit nur 12 Kanälen schon bei 60% CPU-Auslastung bist, dann wird das mit 100 Kanälen mit der Hardware womöglich nix.
    Oder kannst Du die Berechnungsschritte auf mehrere Zyklen verteilen? (Du zeigst uns ja offensichtlich nicht das ganze Programm)
    Du könntest in jedem Zyklus die Berechnungen für nur 1 Kanal machen (dann hättest Du theoretisch max 50ms Zeit je Kanal): dazu in jedem Zyklus nur die Zählerstands-Berechnung machen und bei >=100 die Zeit merken und den Index der Instanz in einen FIFO (oder Ringpuffer) schreiben. Die Instanz schaut in den FIFO, ob der erste/älteste Eintrag der eigene Index ist und macht nur dann die komplette Berechnung.

    Verbessert sich etwas, wenn Du die 12 Instanzen sequentiell hintereinander aufrufst anstatt in der FOR-Schleife?

    Harald
    Es ist immer wieder überraschend, wie etwas plötzlich funktioniert, sobald man alles richtig macht.

    FAQ: Linkliste SIMATIC-Kommunikation über Ethernet

  7. #7
    Registriert seit
    07.06.2007
    Beiträge
    143
    Danke
    2
    Erhielt 24 Danke für 24 Beiträge

    Standard

    Also ich würde das gar nicht über die Systemzeit machen, sondern einfach nur über TIME(). Das ist die Zeit die nach einschalten der PLC hochläuft damit kannst du einfacher rechnen und brauchst keine LREAL usw.

    Einfach:
    Code:
    CASE step OF
    0:
    tStartTime := TIME();
    step := 1;
    
    1:
    iDifPuls := iPlsInputVal - iMeas_PulsAlt;
    IF iDifPuls >= 100 THEN 
    
    
    tDiffTime := TIME() - tStartTime;
    step := 0;
    END_IF;
    
    END_CASE;
    Wenn ich dein Code richtig gelesen habe interessiert dich ja nicht die tatsächliche Uhrzeit also Montag, Tag, Jahr, Stunde, Minute..., sondern nur die Zeitdifferenz. Das kannst du mit Time auch gut machen. Wenn du eine CPU hast die mit Gleitkommawerten nicht so schnell rechnen kann gibt es auch noch eine Möglichkeit gar nicht über Zeiten zu rechnen, sondern in Zyklen (vorausgesetzt du hast deine Task auf Zyklisch gestellt und nicht freilaufend!)
    Dann würde ich das so machen:

    Code:
    VAR
    iInit : INT := 0;
    tCycleTime  : TIME;
    iCycleTimeInMS : INT := 0;
    step : INT:=0;
    iCycleCounter : INT := 0;
    END_VAR
    
    // einmalige Ermittlung der Zykluszeit
    case iInit of
    0:
    tCycleTime := TIME();
    iInit := 1;
    
    1:
    tCycleTime := TIME() - tTaskTime;
    iCycleTimeInMS := TIME_TO_INT(tCycleTime); // Zykluszeit als Int und in MS als Mulitplikationsbasis
    iInit := 999;
    
    END_CASE;
    
    // erst rest durchlaufen wenn die Zykluszeit ermittelt wurde
    IF iCycleTimeInMS  = 0 THEN
    RETURN;
    END_IF;
    
    
    // Nun dein Kram machen
    CASE step OF
    0:
    iCycleCounter  := 0;
    step := 1;
    
    1:
    iCycleCounter := iCycleCounter  +1; // jeden Zyklus + 1 dazuzählen 
    
    iDifPuls := iPlsInputVal - iMeas_PulsAlt;
    IF iDifPuls >= 100 THEN 
    
    
    iDiffTime := iCycleCounter * iCycleTimeInMS;
    step := 0;
    END_IF;
    
    END_CASE;
    Dann bist du (fast) komplett von den Zeiten, LongReals,... weg.

  8. #8
    Registriert seit
    24.02.2009
    Beiträge
    1.242
    Danke
    23
    Erhielt 276 Danke für 235 Beiträge

    Standard

    Also erstmal:
    Der Baustein GETSYSTEMTIME trägt so gut wie garnichts zu deiner Systemlast bei. Ich habe den zum Test mal 1000 mal in einer FOR Schleife auf meinem CX5010 laufen lassen und die CPU-Last ist nichtmal um 1% gestiegen.

    Was bei dir aber ordentlich Rechenleistung zieht ist dein rum gemähre mit 64 Bit Werten die du garnicht brauchst. Du rechnest einen Zeitstempel mit 100ns Auflösung auf eine Auflösung von 1 Sekunde runter. Allerding sind die Last-Spitzen auch bei 500 INT64 Operationen und Konvertierungen zu LREAL pro Zyklus noch lange nicht so krass wie bei dir. Und die CP6606 ist so langsam auch nicht.

    Wenn du bloss 1s Ticks brauchst, dann schaff dir deine eigene Zeitbasis. Z.B. du addierst die Sekunden timergesteuert auf etc.
    Sänd from mei Kombjudder mitse Dastadurr.

  9. #9
    Registriert seit
    10.08.2012
    Beiträge
    245
    Danke
    0
    Erhielt 70 Danke für 66 Beiträge

    Standard

    Hab vor kurzen einen Artikel gelesen um den es ging "EINFACH" zu denken. Fällt mir so spontan bei deinem Code ein.

    GetSystemTime gibt dir zwar Zeiten im 100nsec Format aber so what. Die Systemgenauigkeit des CP6606 liegt deutlich darüber.
    LREAL-Arithemetik ist somit nett aber in dem Fall für die Katz und zudem Resourcenfressend.
    Dich interessiert das die EL1512 möglichst äquidistant abgefragt. Diese Äquidistanz kannst du dann für die Berechnung nutzen. Wenn du mehr brauchst dann bist du bei Distributed Clocks und die PC-Uhrzeit wird irrelevant. Dein CP6606 wäre dann auch nicht mehr die geeignete Platform.

    Also
    1) IO am Taskanfang definieren -> beim Zeitinterrupt wird zuerst der EtherCAT gelesen/geschrieben, danach erst deine SPS. Hintergrund: Du hast eine bessere Synchronität des EtherCATs
    2) Berechne dein Gesamtzeit einfach mit "Anzahl der Zyklen" mit Zykluszeit der PLC. Anzahl der Zyklen wäre nur ein Zähler den du jeweils Inkrementierst.
    Die Zykluszeit kannst du dir auch auslesen und somit Konfigurationsunabhängig nutzen.

    Guga

  10. #10
    Registriert seit
    07.06.2007
    Beiträge
    143
    Danke
    2
    Erhielt 24 Danke für 24 Beiträge

    Standard


    Zuviel Werbung?
    -> Hier kostenlos registrieren
    ... ich glaube jetzt haben wir hier 3 mal die selbe Lösung stehen

Ähnliche Themen

  1. MP370: CPU-Auslastung anzeigen
    Von PeterEF im Forum HMI
    Antworten: 4
    Letzter Beitrag: 30.04.2013, 16:47
  2. Antworten: 4
    Letzter Beitrag: 12.12.2011, 16:44
  3. BK9050 zu hohe Netzwerk Auslastung?
    Von daniel1987 im Forum CODESYS und IEC61131
    Antworten: 4
    Letzter Beitrag: 05.09.2011, 07:44
  4. CA01 10/2009 -> Hohe CPU Auslastung
    Von rs-plc-aa im Forum PC- und Netzwerktechnik
    Antworten: 8
    Letzter Beitrag: 17.12.2009, 17:14
  5. C++ Programm zu hohe CPU auslastung
    Von demmy86 im Forum Hochsprachen - OPC
    Antworten: 16
    Letzter Beitrag: 13.12.2008, 12:48

Stichworte

Lesezeichen

Berechtigungen

  • Neue Themen erstellen: Nein
  • Themen beantworten: Nein
  • Anhänge hochladen: Nein
  • Beiträge bearbeiten: Nein
  •