TIA wo geht mein Datenarbeitsspeicher hin?

vollmi

Level-3
Beiträge
5.763
Reaktionspunkte
1.707
Zuviel Werbung?
-> Hier kostenlos registrieren
Ich habe grade ein Speicherproblem das wohl standard ist ich aber nicht verstehe. Jetzt bin ich aber bei ner CPU am Datenarbeitsspeicheranschlag und weiss nicht warum und wo ich optimieren könnte.

Ich habe alle Zugriffe auf Optimiert und dachte erst obwohl ich so oft wie möglich INOUT verwende, dass da irgendwo dann noch ein CallByValue gemacht wird, aber das kann ich nahezu ausschliessen. Was mir jetzt aber aufgefallen ist, sind die exorbitanten Datenarbeitspeicherverbräuche von einfachen Funktionsbausteinen. Ich habe in meinen Programmen, sehr viele kleinstbausteine die mir einfachste Funktionen kapseln so in dieser Art:

Code:
FUNCTION_BLOCK "FB_Timestamper"{ S7_Optimized_Access := 'TRUE' }
VERSION : 0.1
   VAR_INPUT 
      Alarm { ExternalAccessible := 'False'; ExternalVisible := 'False'; ExternalWritable := 'False'} : Bool;
   END_VAR


   VAR_IN_OUT 
      localtime {InstructionName := 'DTL'; LibVersion := '1.0'} : DTL;
      Timestamp {InstructionName := 'DTL'; LibVersion := '1.0'} : DTL;
   END_VAR


   VAR 
      merk { S7_SetPoint := 'True'} : Bool;
   END_VAR




BEGIN
	IF #Alarm AND NOT #merk THEN
	    #Timestamp := #localtime;
	END_IF;
	#merk := #Alarm;
END_FUNCTION_BLOCK

Dieser Baustein benötigt simple 172 Byte. wenn ich als Flankenmerker die R_Trig Funktion verwende dann sinds locker 188 Byte.
Wozu wird dieser Speicher verwendet? und wie kann ich das optimieren?

Zum verständnis, den Timestamp nutze ich in einem Baustein in nem Array[0..47] deklariert und diesen Baustein rufe ich dann über 100 Mal auf. Da ballert mir dieser minibaustein alleine den Datenarbeitsspeicher zu. Ich kann die Funktion jetzt natürlich in eine For schleife packen und da einfach n Array dafür hernehmen das benötigt natürlich nur noch einen Bruchteil an speicher, aber das Programm wird dann halt unübersichtlicher. Ich mag die minifunktionen und Minibausteine eigentlich.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
nein nicht exakt. Wenn ich den fb als multiinstanz einfüge. wächst der dazugehörige IDB pro multiinstanz "nur" um 112 byte.

Wenn ich da 10 Instanzen des stampers drin habe, hat der IDB des aufrufenden FBs (der aber nix anderes instanziert hat) schon 1292 bytes
 
Zuletzt bearbeitet:
jetzt wird's noch lustiger.
Ich habe den Baustein also 10 mal multiinstanziert. Der IDB des aufrufenden Bausteins benötigt 1292 Bytes
jetzt füge ich im Stat des timestampers ein Byte hinzu. Der IDB des aufrufenden Bausteins benötigt immer noch exakt 1292 Bytes
ändere ich das Byte in ein Word dann benötigt der IDB des aufrufenden Bausteins auf einmal 1372 Bytes. was zum...
 
jetzt füge ich im Stat des timestampers ein Byte hinzu. Der IDB des aufrufenden Bausteins benötigt immer noch exakt 1292 Bytes
Vielleicht war vor die Erweiterung 1 Byte Platz übrig. Die Daten werden zusammengepackt nacheinander je nach Datentyp. BOOLS, BYTEs, WORDs usw. Das kann man nicht selber betrachten wenn die Zugriff 'optimiert' ist, aber so funktioniert es laut die Online Hilfe in TIA.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Vielleicht war vor die Erweiterung 1 Byte Platz übrig. Die Daten werden zusammengepackt nacheinander je nach Datentyp. BOOLS, BYTEs, WORDs usw. Das kann man nicht selber betrachten wenn die Zugriff 'optimiert' ist, aber so funktioniert es laut die Online Hilfe in TIA.

Das erklärt aber nicht, warum aus 10 bytes in 10 instanzen verteilt auf einmal 80 Bytes werden, wenn ich aus den 10 Bytes in den instanzen ein Word mache.
 
Zum verständnis, den Timestamp nutze ich in einem Baustein in nem Array[0..47] deklariert und diesen Baustein rufe ich dann über 100 Mal auf. Da ballert mir dieser minibaustein alleine den Datenarbeitsspeicher zu. Ich kann die Funktion jetzt natürlich in eine For schleife packen und da einfach n Array dafür hernehmen das benötigt natürlich nur noch einen Bruchteil an speicher, aber das Programm wird dann halt unübersichtlicher.
Da Du den FlankenMerker ...
Code:
    IF #Alarm AND NOT #merk THEN
        #Timestamp := #localtime;
    END_IF;
    #merk := #Alarm;
... '#merk' sowieso schon "zu Fuss" machst, könntest Du ihn in jedem Element des Array unterbringen, statt in jeweils einer eigenen Instanz des FB - der FB(/FC) muss ja nicht wissen, dass es sich um FlankenMerker handelt. Aber - ich glaube - das meinst Du schon mit Deiner unübersichtlichen Variante.
Ich verstehe noch nicht so ganz, ob Du an einer einzigen Flanke die Bearbeitung aller ArrayElemente auslösen willst oder, ob für jedes ArrayElement wirklich eine eigene FlankenErkennung gebraucht wird ...
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Code:
   VAR_IN_OUT 
      localtime {InstructionName := 'DTL'; LibVersion := '1.0'} : DTL;
      Timestamp {InstructionName := 'DTL'; LibVersion := '1.0'} : DTL;
   END_VAR
Brauchst Du die DTL-Variablen als IN_OUT? Hast Du mal ausprobiert, die als INPUT oder OUTPUT zu übergeben?

Ein DTL ist laut Dokumentation nur 12 Byte groß. Um diese 12 Byte "optimiert" byReference zu übergeben werden 84 Byte in der Instanz gebraucht??? :confused:
Wird da womöglich auch noch das Symbol der außen angeschalteten Variable mit übergeben? Verwendest Du in dem MiniFB GetSymbolName, GetInstanceName oder ähnliches?

Harald
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Was mal interessant ist: Bei einer 1200er die ich hier habe benötigt der Instanz-DB zu deinem FB 6 Byte Arbeitsspeicher und 1416 Byte im Ladespeicher. Wobei meine alte Version der 1200er für ein Bool wirklich nur ein Bit benötigt und nicht ein Byte wie bei der 1500er.
 
Zeig mal ein Beispiel wie du die gezeigte Funktion oben aufrufst. Dann versuche ich das mal zu analysieren.

Code:
REGION Timestamp    FOR #index := 0 TO 7 DO
        #timestamper[#index](Alarm := BYTE_TO_BOOL(SHR_BYTE(IN := #Signal_Obj_LST.Status.Status1,N := #index)),
                             localtime := #localtime,
                             Timestamp := #Signal_WEB.Status.Status1_Timestamp[#index]);
    END_FOR;
    FOR #index := 0 TO 7 DO
        #timestamper[#index + 8](Alarm := BYTE_TO_BOOL(SHR_BYTE(IN := #Signal_Obj_LST.Status.Status2, N := #index)),
                                 localtime := #localtime,
                                 Timestamp := #Signal_WEB.Status.Status2_Timestamp[#index]);
    END_FOR;
    FOR #index := 0 TO 31 DO
        #timestamper[#index + 16](Alarm := DWORD_TO_BOOL(SHR_DWORD(IN := #Signal_Obj_LST.Status.Status_Internal, N := #index)),
                                  localtime := #localtime,
                                  Timestamp := #Signal_WEB.Status.Status_Internal_Timestamp[#index]);
    END_FOR;
END_REGION

der ganze Baustein wird an die 100 Mal aufgerufen. was natürlich eine Speicherexplosion verursacht.

ich hab das jetzt so umgebaut. nur um mal den speicherverbrauch so anzutesten. Das ist natürlich ein gewaltiger unterschied.
Die funktion hab ich jetzt noch nicht geprüft habe hoffentlich keinen denkfehler drin. ist schon spät.

Code:
REGION Timestamp
    FOR #index := 0 TO 7 DO
        IF BYTE_TO_BOOL(SHR_BYTE(IN := #Signal_Obj_LST.Status.Status1,N := #index)) AND NOT #timestamper[#index] THEN
                            #Signal_WEB.Status.Status1_Timestamp[#index] := #localtime;
        END_IF;
        #timestamper[#index] := BYTE_TO_BOOL(SHR_BYTE(IN := #Signal_Obj_LST.Status.Status1, N := #index));
    END_FOR;
    FOR #index := 0 TO 7 DO
        IF BYTE_TO_BOOL(SHR_BYTE(IN := #Signal_Obj_LST.Status.Status2, N := #index)) AND NOT #timestamper[#index + 8] THEN
            #Signal_WEB.Status.Status2_Timestamp[#index] := #localtime;
        END_IF;
        #timestamper[#index + 8] := BYTE_TO_BOOL(SHR_BYTE(IN := #Signal_Obj_LST.Status.Status2, N := #index));
    END_FOR;
    FOR #index := 0 TO 31 DO
        IF DWORD_TO_BOOL(SHR_DWORD(IN := #Signal_Obj_LST.Status.Status_Internal, N := #index)) AND NOT #timestamper[#index + 16] THEN
            #Signal_WEB.Status.Status_Internal_Timestamp[#index] := #localtime;
        END_IF;
        #timestamper[#index + 16] :=DWORD_TO_BOOL(SHR_DWORD(IN := #Signal_Obj_LST.Status.Status_Internal, N := #index));
    END_FOR;
END_REGION
 
Die 172 Byte scheinen für Verwaltungsinformationen draufzugehen. Wenn du einen komplett leeren FB erstellst, also ohne Variablen, dann benötigt der Instanz-DB ebenfalls 172 Byte.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Die 172 Byte scheinen für Verwaltungsinformationen draufzugehen. Wenn du einen komplett leeren FB erstellst, also ohne Variablen, dann benötigt der Instanz-DB ebenfalls 172 Byte.

sowas ähnliches scheint es zu sein, macht aber Bausteine wie r_trig irgendwie absurd.
und ich wüsste gerne was das für verwaltungsinfos sind. Sowas müsste ja eher dem codespeicher zugeordnet werden.

sowas fällt mir nach zehn jahren tia auf. ;)*ROFL*
 
Das handhabt auch noch jede Steuerung und Firmware anders. Wenn ich einen FB mit einem Byte im Stat-Bereich erstelle, und dann ein FB mit 4 Multiinstanzaufrufen davon, dann benötigt der Instanz-DB bei:
- S7-1515: 620 Bytes
- S7-1215 Firmware 4.2: 556 Bytes
- S7 1214 Firmware 2.2: 38 Bytes

Bei der 1214 mit Firmware 2.2 lassen sich aber aus dem Programm heraus auch keine Attribute wie Länge oder DB-Attribute lesen. Wobei das auch nur 1x UDInt und 1x Byte sind die ich da erhalte.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
sowas ähnliches scheint es zu sein, macht aber Bausteine wie r_trig irgendwie absurd.
Diese vorgefertigten FlankenErkennungen waren mir von Anfang an unsympathisch (ja, ich bin sooo altmodisch :ROFLMAO:).

Also ich fühl mich da bei den Thema fast bei der s5 daheim
Oooch? Lang ist's her, aber waren derartige Probleme schon bei der S5 erfunden? Speicher war unverschämt teuer und konnte nicht beliebig erweitert werden, aber überschaubarer fand ich's allemal. ;)
 
Zurück
Oben