Attribut 'estimated-stack-usage'

Das Pragma übergibt einen Schätzwert für den Stackgrößenbedarf.

Methoden mit rekursivem Aufruf halten einer Stackprüfung nicht stand, weil der Stackverbrauch nicht ermittelt werden kann. Folglich wird eine Warnung für diese Methoden ausgegeben. Um die Warnung zu unterdrücken, können Sie der Methode mit Hilfe des Attributs einen Schätzwert in Bytes für den Stackgrößenbedarf mitgeben. Dann durchläuft die Methode die Stackprüfung erfolgreich.

Syntax: {attribute 'estimated-stack-usage' := '<estimated stack size in bytes>'}

Einfügeort: Erste Zeile über dem Deklarationsteil der Methode

Beispiel:

{attribute 'estimated-stack-usage' := '99'} // 99 bytes
METHOD SampleMethod : INT
VAR_INPUT
END_VAR

Ausgegebene Warnung:

Die Warnung, die für rekursive Methoden ohne Attribut 'estimated-stack-usage' ausgegeben wird, lautet:

„C0298: Berechnung des Stackverbrauchs aufgrund rekursiver Aufrufe unvollständig, beginnend bei '<FB.Methode>'“

In einem Projekt, das im Vergleich zum letzten Erstellen des Projekts unverändert ist, wird die Warnung nur bei Verwendung des Befehls Projekt neu erstellen ausgegeben (nicht bei Verwendung des Befehls Projekt erstellen).

Einstellung der Stackgröße:

Der Wert der maximal möglichen Stackgröße kann in den Real-Time Einstellungen des TwinCAT-Projekts konfiguriert werden (Registerkarte Settings > Global Task Config/Maximal Stack Size [KB]). Der eingestellte Maximalwert steht dabei nicht komplett dem SPS-Projekt zur Verfügung, sondern wird z. B. ein Teil davon vom TwinCAT-Laufzeitsystem gebraucht.

Attribut 'estimated-stack-usage' 1:

Keine Stackberechnung für Stand-alone SPS-Projekte

Aufgrund der Abkoppelung vom System Manager ist eine Berechnung des Stackverbrauchs für ein Stand-alone SPS-Projekt nicht möglich.

Rekursiver Methodenaufruf

Innerhalb der zugehörigen Implementierung kann sich eine Methode selbst aufrufen: entweder direkt mit Hilfe des THIS-Pointers oder mit Hilfe einer lokalen Variablen für den zugeordneten Funktionsbaustein.

Hinweis

Maschinenstillstand durch möglichen Stacküberlauf

Bei unerwartet tiefen Rekursionen kann es zu einem Stacküberlauf und damit zu einem Maschinenstillstand kommen.

  • Verwenden Sie Rekursionen vorwiegend zur Bearbeitung von rekursiven Datentypen, wie beispielsweise verketteten Listen.

Beispiel: Berechnung der Fakultät

Innerhalb des Funktionsbausteins FB_Factorial wird die Fakultät einer Zahl auf unterschiedliche Weise berechnet. Die verschiedenen Berechnungen finden jeweils in einer eigenen Methode statt.

Bei der Neuerstellung des Projekts erzeugt nur die Methode Recursive die Warnung C0298.

Struktur ST_FactorialResult zur Speicherung der Werte:

// Contains the data of the factorial calculation of nNumber.
TYPE ST_FactorialResult :
STRUCT
    nNumber    : USINT;
    nIterative : UDINT;
    nRecursive : UDINT;
    nPragmaed  : UDINT;
END_STRUCT
END_TYPE

Funktionsbaustein FB_Factorial zur Berechnung der Fakultät:

// Factorial calculation in different ways
FUNCTION_BLOCK FB_Factorial
VAR
     nNumberIterative    : UINT;
END_VAR

Property nNr samt Set-Funktion, zur Übergabe des Parameters für die iterative Methode:

{attribute 'monitoring' := 'variable'}
PROPERTY nNr : UINT
nNumberIterative := nNr;

Iterative Berechnungsmethode:

// Iterative calculation
METHOD PUBLIC Iterative : UDINT
VAR
    nCnt : UINT;
END_VAR
Iterative := 1;
 
IF nNumberIterative > 1 AND nNumberIterative <= 12 THEN
    FOR nCnt := 1 TO nNumberIterative DO
        Iterative := Iterative * nCnt;
    END_FOR;
    RETURN;
ELSE
    RETURN;
END_IF

Rekursive Berechnungsmethode mit Attribut zur Unterdrückung der Warnung C0298:

// Recursive calculation with suppressed warning
{attribute 'estimated-stack-usage' := '200'}
METHOD PUBLIC Pragmaed : UDINT
VAR_INPUT
    nNumber : USINT;
END_VAR
Pragmaed := 1;
 
IF nNumber > 1 AND nNumber <= 12 THEN
    Pragmaed := nNumber * THIS^.Pragmaed(nNumber := (nNumber - 1));
    RETURN;
ELSE
    RETURN;
END_IF

Der Pragma-Parameter zur Angabe des geschätzten Stackgrößenbedarfs wird beispielhaft auf 200 Byte gesetzt. Dem liegt die folgende Berechnung zugrunde:

Stackverbrauch pro Methodenaufruf:
Rückgabewert UDINT + Eingangsparameter USINT + Methodenpointer = 4 Byte + 1 Byte + 8 Byte = 13 Byte

Stackverbrauch bei maximal 12 Aufrufen:
12 * 13 Byte = 156 Byte, aufgerundet auf 200 Byte

Rekursive Berechnungsmethode:

// Recursive calculation
METHOD PUBLIC Recursive : UDINT
VAR_INPUT
    nNumber : USINT;
END_VAR
Recursive := 1;
IF nNumber > 1 AND nNumber <= 12 THEN
    Recursive := nNumber * THIS^.Recursive(nNumber := (nNumber - 1) );
    RETURN;
ELSE
    RETURN;
END_IF

Hauptprogramm MAIN:

PROGRAM MAIN
VAR
    fbFactorial : FB_Factorial;
    stFactorial : ST_FactorialResult := (nNumber := 9);
END_VAR
// Iterativ
fbFactorial.nNr        := stFactorial.nNumber;
stFactorial.nIterative := fbFactorial.Iterative();

// Recursive
stFactorial.nRecursive := fbFactorial.Recursive(nNumber := stFactorial.nNumber);

// Pragmaed
stFactorial.nPragmaed  := fbFactorial.Pragmaed(nNumber := stFactorial.nNumber);