TIA Bausteinschnittstelle - CallByValue/CallByReference - Zykluszeit

mallepalle83

Level-2
Beiträge
21
Reaktionspunkte
1
Zuviel Werbung?
-> Hier kostenlos registrieren
Frage zur Bausteinschnittstelle - CallByValue/CallByReference - Zykluszeit

Hallo alle zusammen,

ich stehe gerade vor dem Problem, dass ich einen Anlagenteil zu programmieren habe, der ständig die selben Funktionen aufruft. Daher möchte ich das Ursprungsprogramm gerne optimieren und alle verwendeten Variablen an die Bausteinschnittstelle der jeweiligen Funktion auslagern und den Baustein somit Instanzfähig machen. Dies ist erstmal nicht neues!

Die Frage bezieht sich Hauptsächlich auf die Funktions-/Arbeitsweise der SPS:
Durch die vielzahl an verwendeten Variablen aus unterschiedlichen DBs muss ich ca. 9 InOut Schnittstellen als UDT definieren.
Jetzt versuche ich die Frage zu formulieren, was mir gerade nicht leicht fällt, daher nutze ich Bilder als Hilfsmittel :razz:

Anbei ein Beispiel einer Möglichen Schnittstelle
Beispiel_01.PNG

Innerhalb der Funktion wird nur auf einzelne Variablen der Schnittstelle gelesen/geschrieben. Zum Beispiel wird innerhalb des Bausteins nur auf die Variable "BeispielSchnittstelle_2.Var4" zugegriffen. Alle anderen Variablen dieser Schnittstell finden keine Verwendung.
Muss nun die SPS bei jedem Bausteinaufruf die "GESAMTE" Schnittstelle lesen oder liest die SPS nur die wirklich verwendeten Variablen? :confused: Die Frage stellt sich mir, da ich diesen Baustein später > 100 mal aufrufen werde, und die Schnittstellen teilweise wesentlich mehr Werte beinhalten, ich jedoch wie gesagt nur einen Bruchteil verwende. Ich habe halt bedenken das in diesem Fall die Zykluszeit unzulässig ansteigt!

SIEHE BILD Vorher/Nachher - Hier nur 3 Variablen des DBs Verwendet, der Rest unberührt.
Beispiel_02.jpg

Ich hoffe ich konnte meine Frage formulieren,
Gruß, Mallepalle
 
Zuletzt bearbeitet:
[h=1]Hallo,
das wird ganz gut in folgendem Dokument von Siemens erklärt.

Programmierleitfaden und Programmierstyleguide für SIMATIC S7-1200 und S7-1500[/h]

Wenn bei einem Bausteinaufruf Strukturen als Durchgangsparameter (InOut) an den aufgerufenen Baustein übergeben werden, werden diese standardmäßig als Referenz übergeben (siehe Kapitel 3.3.2 Übergabe per Referenz (Call-by- reference)).
Dies gilt jedoch nicht, wenn einer der Bausteine die Eigenschaft "Optimierter Zugriff" und der andere Baustein die Eigenschaft "Standardzugriff" hat. Dann werden grundsätzlich alle Parameter als Kopie übergeben (siehe Kapitel 3.3.1 Übergabe per Wert (Call-by-value)).
Der aufgerufene Baustein arbeitet in diesem Fall immer mit den kopierten Werten. Während der Bausteinbearbeitung werden diese Werte möglicherweise verändert und nach Abarbeitung des Bausteinaufrufs wieder auf den ursprünglichen Operanden zurückkopiert.
Dies kann zu Problemen führen, wenn die ursprünglichen Operanden durch asynchrone Prozesse verändert werden, z.B. durch HMI-Zugriffe oder durch Alarm- OBs. Wenn nach der Bausteinbearbeitung die Kopien wieder auf die ursprünglichen Operanden zurückkopiert werden, werden dabei die asynchron durchgeführten Änderungen an den ursprünglichen Operanden überschrieben.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Hallo Coco,
den Abschnitt hatte ich bereits gefunden, jedoch konnte ich diesen nicht eindeutig interpretieren!

Also verstehe ich das richtig, dass es zu keiner Verlängerung der Zykluszeit kommt, da auf die Schnittstelle an der ein beliebig großer DB liegt, nur mittels CallByReference zugegriffen wird und somit falls auf nur eine Variable zugegriffen wird auch nur diese eine geleseen wird und nicht der gesamte DB geladen werden muss?

mfg
 
Also verstehe ich das richtig, dass es zu keiner Verlängerung der Zykluszeit kommt, da auf die Schnittstelle an der ein beliebig großer DB liegt, nur mittels CallByReference zugegriffen wird und somit falls auf nur eine Variable zugegriffen wird auch nur diese eine geleseen wird und nicht der gesamte DB geladen werden muss?
Es ist richtig, daß bei CallByReference nur auf die Variablen zugegriffen wird, auf die der aufgerufene Baustein tatsächlich zugreift. Die Übergabe des Bereichs dauert gleich lange, egal wie groß der übergebene Bereich ist, weil nur ein Pointer (und evtl. eine Typinformation) übergeben wird, es wird nicht die gesamte Struktur kopiert - man spart hier sogar zunächst Programm-Ausführungszeit gegenüber dem Struktur kopieren bei Übergabe by-Value, und man spart Speicherplatz für die Parameterübergabe. Dafür ist es dann aber im aufgerufenen Baustein aufwändiger und langsamer, auf die Variablen der übergebenen Struktur zuzugreifen. Je nachdem, wieviele unbenötigte Daten die Struktur enthält und ob nur eine oder mehrere Variablen/Strukturen by-Reference übergeben werden, kann es schneller sein sein, die gesamte Struktur beim Aufruf oder im aufgerufenen Code zunächst komplett auf lokalen Speicher umzukopieren und dann mit den lokalen Kopien zu arbeiten.

Wenn im Extremfall nur eine Variable benötigt wird, dann ist es in der Regel am effizientesten nur diese eine Variable by-Value zu übergeben.

Eine fundierte Aussage, welche Art Parameterübergabe im speziellen Fall für die gesamt-Programmausführungszeit schneller ist, kann man nur treffen, wenn man die Programmausführungszeiten der verschiedenen Varianten misst und vergleicht. Besonders wenn der verwendete Compiler undokumentierte Optimierungen vornimmt.


Was man auch beachten muß: bei Übergabe by-Reference bekommt man beim Lesen der übergebenen Variablen im Baustein nicht den Wert vom Zeitpunkt beim Bausteinaufruf sondern den Wert vom Zeitpunkt der Leseoperation. Wenn asynchrone/multitasking Prozesse die referenzierten Daten verändern, dann können die Daten während der Bausteinbearbeitung inkonsistent werden, z.B. zweimal die selbe Variable lesen kann 2 verschiedene Werte liefern.


Noch ein Aspekt: Für Programmverständnis und Aussagekraft der Referenzdaten ist es oft günstiger, wenn nur die tatsächlich benötigten Variablen übergeben werden und nicht Pointer auf große Ansammlungen vieler Variablen, wo dann an jeder Referenzstelle auf die meisten referenzierten Variablen überhaupt gar nicht zugegriffen wird.

Harald
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Hallo Harald, ich arbeite an einem Projekt, in dem ein Baustein Multiinstanzfähig sein soll und somit Schnittstellendaten über INOUT als UDT übergeben werden. Aktuell sind alle Bausteine noch nicht optimiert und werden somit als CallByReference übergeben. Innerhalb des FB´s gibt es weitere Unterbausteine, an denen auch Teile der Schnittstelle übergeben werden.

Wenn ich jetzt einen Gesamtüberblick über den Zugriff auf eine bestimmt Variable haben möchte, dann bekomme ich nur einen Querverweis im jeweiligen Baustein. Je nachdem wie weit die Verschachtelung in Unterbausteine geht, ist es schwer einen Überblick zu erhalten.

Ich habe mein Anliegen auch schon beim Siemens Support gemeldet, bekomme bis jetzt aber noch keine brauchbaren Informationen.

Es geht zwar etwas über die eigentliche Frage hinaus, es wäre aber super wenn mir jemand dazu einem Tipp geben könnte.

Vielen Dank,
COCO
 
Ich habe mein Anliegen auch schon beim Siemens Support gemeldet, bekomme bis jetzt aber noch keine brauchbaren Informationen.
Dazu gibt es auch keine brauchbaren Informationen.
Es gilt einfach, je weniger Schnittstelle umso schneller :p
Call-by-Value erfordert beim Aufruf ein einmaliges Umkopieren und ist dafür beim Zugriff schneller.
Call-by-Reference arbeitet mit Zeigern, braucht kein Umkopieren, ist aber beim Zugriff langsamer.
Ich persönlich bin kein Freund von aufgeblasenen Schnittstellen mit vielen unbenutzen Variablen.
Macht das Ganze nur schlecht lesbar.

Gruß
Blockmove
 
Hallo Blockmove,
Es ist klar, dass der Zugriff auf Variablen bei Call-by_Value länger dauert. Das ist Aufgrund der Multiinstanzfähigkeit und der symbolischen Programmierung nicht anders möglich. Bei den neuen Steuerungen läuft das aber sehr schnell und macht kein Problem. Mir geht es nur um einen globalen Querverweis. Durch die Programmierung ist doch der Zugriff eindeutig und der Querverweis könnte auch unterlagerte Bausteine beinhalten. Wenn das aktuell bei der V15 nicht geht, dann sehe ich das als einen Mangel von Siemens der behoben oder verbessert werden sollte.
Gruß, COCO
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Zum Suchen:

Es ist leider so, dass man die, per Struktur übergebenen untergeordneten Variablen über den Querverweis nicht findet, nur innerhalb des Bausteins. Siemens weiß das seit Jahren, Lösungen für den Querverweis gab es bisher leider nicht.
Inzwischen gibt es immerhin eine Art Volltextsuche (Projektsuche genannt), erreichbar über Ctrl F F.
Wenn man seine Variablennamen gut strukturiert, kann man darüber sehr viel machen. Eine Einschränkung des Suchraumes ist dort ebenfalls möglich.
 
Bei einem FB ist es meiner Meinung nach überhaupt nicht sicher, dass es da einen Unterschied in der Geschwindigkeit beim Zugriff byVal / byRef gibt.
Man weiß ja nicht was Siemens da intern überhaupt so zaubert. Aber grundsätzlich wird es bei Zugriff auf eine FB-Instanz-Variable auch immer einen Zeigerzugriff geben, nämlich den auf den Instanz-DB plus Offset der Instanzvariable. Etwa so wie es in C++ bei einem Objekt der this-Zeiger ist, über den dann der Zugriff auf die Membervariablen erfolgt. Ob es dann einen großen Geschwindigkeitsunterschied gibt, hängt davon ab, ob überhaupt, und wenn, wie gut Siemens den generierten Code optimiert.
 
Egal wie umständlich auf den Übergabeparameter zugegriffen wird, da muß es grundsätzlich einen Unterschied geben, weil bei byVal schon der erste Speicherzugriff auf den Übergabeparameter den Wert der Variable liefert, während bei byRef erst der Zeiger gelesen wird und noch ein weiterer Speicherzugriff auf die eigentliche Variable nötig ist.

Harald
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Bei einem FB ist es meiner Meinung nach überhaupt nicht sicher, dass es da einen Unterschied in der Geschwindigkeit beim Zugriff byVal / byRef gibt.
Ich hab mal mit ner 1200er damit rumgespielt. Da gab es Unterschiede.
Bei ner 1500er und einer Mischung aus optimierten und nicht optimierten Bausteinen waren die Ergebnisse nicht wirklich reproduzierbar.
War damals mit TIA V12. Kann heute schon wieder agnz anders aussehen.
 
Egal wie umständlich auf den Übergabeparameter zugegriffen wird, da muß es grundsätzlich einen Unterschied geben, weil bei byVal schon der erste Speicherzugriff auf den Übergabeparameter den Wert der Variable liefert, während bei byRef erst der Zeiger gelesen wird und noch ein weiterer Speicherzugriff auf die eigentliche Variable nötig ist.
Aber nicht bei einem FB. Die Instanz-DB Nummer ist ja nicht mehr als ein Zeiger auf einen Speicherbereich.
D.h. der Zugriff auf eine Instanzvariable erfolgt aus "Instanz-DB Basisadresse" + "Offset der Variable". Wenn z.B. eine Struct byRef dann "Basisadresse Struktur" + "Offset der Variable".

Ob das bei der 1200/1500 überhaupt so funktioniert ist ja überhaupt nicht klar, d.h. ob der Code überhaupt bis auf Maschinensprache übersetzt wird, oder ob ein Zwischencode in einer Art VM interpretiert wird, ähnlich wie bei der .Net CLI. Meine Vermutung ist letzteres.
 
Aber nicht bei einem FB. Die Instanz-DB Nummer ist ja nicht mehr als ein Zeiger auf einen Speicherbereich.
D.h. der Zugriff auf eine Instanzvariable erfolgt aus "Instanz-DB Basisadresse" + "Offset der Variable". Wenn z.B. eine Struct byRef dann "Basisadresse Struktur" + "Offset der Variable".
Das ändert aber doch nichts daran, daß nachdem endlich der Übergabeparameter gelesen ist, bei byVal schon der Variablenwert vorliegt, bei byRef aber noch ein zweiter Speicherzugriff zum Lesen des Variablenwertes nötig ist. Es ist quasi ein doppelt indirekter Speicherzugriff, der nicht zusammengefasst werden kann, weil auch FB können nicht "ahnen" welcher Pointer da zur Laufzeit außen angeschaltet ist und müssen den erst lesen ;)
Höchstens Folgezugriffe in die selbe Struktur können schneller ausgeführt werden, wenn der einmal gelesene Pointer in einem Register gehalten werden kann.

Harald
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Höchstens Folgezugriffe in die selbe Struktur können schneller ausgeführt werden, wenn der einmal gelesene Pointer in einem Register gehalten werden kann.
Hab ich doch in meinem ersten Post schon genau so geschrieben, dass es davon abhängt wie gut Siemens den Code übersetzt.
Und die Anfangsadresse in einem Register behalten konnte sogar schon der SCL-Compiler aus Step7-Classic. Was dazu führt, dass dort bei einem FB Zugriffe auf Struct-Variablen an der InOut-Schnittstelle schneller sind, als es jemand in AWL überhaupt programmieren könnte.
 
Und die Anfangsadresse in einem Register behalten konnte sogar schon der SCL-Compiler aus Step7-Classic. Was dazu führt, dass dort bei einem FB Zugriffe auf Struct-Variablen an der InOut-Schnittstelle schneller sind, als es jemand in AWL überhaupt programmieren könnte.
Das kommt ja wohl auf die Fähigkeiten des AWL-Programmierers drauf an. Ich habe noch kein classic-SCL-Programm gesehen, was schneller zugreift als ich es in AWL könnte... ;)

Harald
 
Das kommt ja wohl auf die Fähigkeiten des AWL-Programmierers drauf an. Ich habe noch kein classic-SCL-Programm gesehen, was schneller zugreift als ich es in AWL könnte...

Aber nicht mehr symbolisch sondern nur mit Absolutadressen und somit unwartbar. Direkt in AWL hingeschrieben bekommst du das folgende Beispiel in AWL nur schwerlich kleiner
Code:
{SCL_CreateDebugInfo := 'n'; SetOKFlag := 'n' ; OptimizeObjectCode := 'y'}

FUNCTION_BLOCK FB10000

VAR_IN_OUT
    data : STRUCT 
        res1 : INT;
        arg1 : INT;
        arg2 : INT;
        arg3 : INT;
    END_STRUCT;
END_VAR

BEGIN

data.res1 := data.arg1 + data.arg2 + data.arg3;

END_FUNCTION_BLOCK

Und nicht den AWL-Code vergleichen, sondern den MC7-Code. SCL-Code 90 Bytes, 1:1 in AWL 210 Bytes, und das ohne BIE-Bit Handling.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Ich habe noch kein classic-SCL-Programm gesehen, was schneller zugreift als ich es in AWL könnte... ;)
Wenn man sich den von SCL erzeugten AWL / MC7 Code bei Classic anschaut, dann holst du da nicht mehr viel raus. Zumindest bei ner 300er.
Bei ner 400er kannst du vielleicht noch etwas mehr optimieren.
 
Aber nicht mehr symbolisch sondern nur mit Absolutadressen und somit unwartbar. Direkt in AWL hingeschrieben bekommst du das folgende Beispiel in AWL nur schwerlich kleiner
[...]
Und nicht den AWL-Code vergleichen, sondern den MC7-Code.
Was willst Du mit Deinem einem Beispiel eigentlich beweisen?
Und was meinst Du mit dem "MC7-Code vergleichen"? Kann Dein SCL-Compiler direkt MC7-Code erzeugen ohne AWL-Umweg oder erzeugt es Code, den man mit AWL nicht erzeugen kann?

Interessanter wäre ja, wenn Du die Ausführungszeit des Beispiels misst und dann die 4 Parameter einzeln byVal übergibst und ebenfalls misst - welche Variante ist schneller?
Dann erweitere mal Dein Beispiel auf 9 IN_OUT-Structs (wie der TE wollte) und addiere aus jedem Struct nur eine Variable. Da hat der SCL-Compiler kaum eine Chance zum optimieren.

Dein SCL-Beispiel mit ByRef-Übergabe braucht 90 Bytes Arbeitsspeicher, direkt in AWL bekomme ich das mit 76 Bytes Arbeitsspeicherbedarf hin - zugegeben, genauso unwartbar wie das SCL-Kompilat, wenn man die SCL-Quelle nicht hat.

Dein Beispiel zum Vergleich mit einer Parameterübergabe byVAL:
Code:
{SCL_CreateDebugInfo := 'n'; SetOKFlag := 'n' ; OptimizeObjectCode := 'y'}

FUNCTION_BLOCK FB10001
VAR_INPUT
    data : STRUCT 
        arg1 : INT;
        arg2 : INT;
        arg3 : INT;
    END_STRUCT;
END_VAR
VAR_OUTPUT
    res1 : INT;
END_VAR

BEGIN

res1 := data.arg1 + data.arg2 + data.arg3;

END_FUNCTION_BLOCK
Das läßt sich in AWL sogar symbolisch effizienter programmieren:
Code:
      L     #data.arg1
      L     #data.arg2
      +I    
      L     #data.arg3
      +I    
      T     #res1

Hier der Vergleich classic-SCL für S7-300, FB multinstanzfähig.
Code:
Beispiel   Arbeitspeicher   MC7   Ausführungszeit
               Bytes       Bytes

ByRef SCL        90          54          ?        Übergabe 1 Struct, Summe 3 INT aus dem Struct
ByRef AWL        76          40          ?

ByVal SCL        68          32          ?        Übergabe 1 Struct, Summe 3 INT aus dem Struct
ByVal AWL        58          22          ?

ByRef 9 SCL     302         266          ?        Übergabe 9 Struct, Summe je ein INT aus jedem Struct
ByVal 9 SCL     104          68          ?

TIA-SCL habe ich gerade nicht greifbar. Vielleicht hat ja jemand Lust die selben Vergleiche für S7-1x00 zu machen.


Auch wenn ich mich nochmal wiederhole: der erste Zugriff auf eine byRef übergebene Variable ist prinzipiell langsamer als auf eine byVal übergebene Variable. Ob weitere Zugriffe auf diese Variable oder andere Variablen in der selben Struktur dann tatsächlich schneller sind hängt von mehreren Faktoren ab - doch auch diese Zugriffe können dann nicht schneller sein als Zugriffe auf byVal-Übergabeparameter, höchstens gleich schnell. Übergabe byRef bleibt also prinzipiell immer ein gewisses "etwas" langsamer.

Harald
 
Hallo Ralle, das mit der Volltextsuche ist ein gutes Workaround. Es ist natürlich etwas umständlich und die Art der Verwendung wird nicht mit angezeigt aber besser als nichts. Ich werde diesen Änderungswusch bzw. Verbesserungsvorschlag zu den Querverweisen auch an anderer Stelle bei Siemens vortragen. Wenn es zu diesem Thema von vielen Stellen Reklamationen an Siemens gibt, wird sich hoffentlich mal was bewegen.
Gruß, COCO
 
Zurück
Oben