Sind skalare und strenge Typen in PHP7 eine leistungssteigernde Funktion?

Lesezeit: 5 Minuten

Benutzer-Avatar
igorsantos07

Seit PHP7 können wir das jetzt Verwenden Sie skalaren Typhinweis und fragen Sie nach strengen Typen pro Datei. Gibt es Leistungsvorteile durch die Verwendung dieser Funktionen? Wenn ja, wie?

Rund um die Interwebs habe ich nur konzeptionelle Vorteile gefunden, wie zum Beispiel:

  • genauere Fehler
  • Vermeidung von Problemen mit unerwünschter Typumwandlung
  • mehr semantischer Code, Vermeidung von Missverständnissen bei der Verwendung von Code anderer
  • bessere IDE-Evaluierung des Codes

  • Ein potenziell leistungssteigernder Effekt von skalaren Typhinweisen besteht darin, dass die Typumwandlung erzwungenermaßen früh erfolgt, was der Fall ist könnte reduzieren Sie die Anzahl der nachfolgenden Güsse.

    – Nikic

    7. Oktober 2015 um 14:37 Uhr

Benutzer-Avatar
Joe Watkins

Heutzutage verbessert die Verwendung von skalaren und strengen Typen in PHP7 die Leistung nicht.

PHP7 hat keinen JIT-Compiler.

Wenn PHP irgendwann einen JIT-Compiler bekommt, ist es nicht allzu schwierig, sich Optimierungen vorzustellen, die mit den zusätzlichen Typinformationen durchgeführt werden könnten.

Bei Optimierungen ohne JIT sind skalare Typen nur bedingt hilfreich.

Nehmen wir folgenden Code:

<?php
function (int $a, int $b) : int {
    return $a + $b;
}
?>

Dies ist der von Zend dafür generierte Code:

function name: {closure}
L2-4 {closure}() /usr/src/scalar.php - 0x7fd6b30ef100 + 7 ops
 L2    #0     RECV                    1                                         $a                  
 L2    #1     RECV                    2                                         $b                  
 L3    #2     ADD                     $a                   $b                   ~0                  
 L3    #3     VERIFY_RETURN_TYPE      ~0                                                            
 L3    #4     RETURN                  ~0                                                            
 L4    #5     VERIFY_RETURN_TYPE                                                                    
 L4    #6     RETURN                  null

ZEND_RECV ist der Opcode, der Typverifizierung und Zwang für die empfangenen Parameter durchführt. Der nächste Opcode ist ZEND_ADD:

ZEND_VM_HANDLER(1, ZEND_ADD, CONST|TMPVAR|CV, CONST|TMPVAR|CV)
{
    USE_OPLINE
    zend_free_op free_op1, free_op2;
    zval *op1, *op2, *result;

    op1 = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R);
    op2 = GET_OP2_ZVAL_PTR_UNDEF(BP_VAR_R);
    if (EXPECTED(Z_TYPE_INFO_P(op1) == IS_LONG)) {
        if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_LONG)) {
            result = EX_VAR(opline->result.var);
            fast_long_add_function(result, op1, op2);
            ZEND_VM_NEXT_OPCODE();
        } else if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_DOUBLE)) {
            result = EX_VAR(opline->result.var);
            ZVAL_DOUBLE(result, ((double)Z_LVAL_P(op1)) + Z_DVAL_P(op2));
            ZEND_VM_NEXT_OPCODE();
        }
    } else if (EXPECTED(Z_TYPE_INFO_P(op1) == IS_DOUBLE)) {
        if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_DOUBLE)) {
            result = EX_VAR(opline->result.var);
            ZVAL_DOUBLE(result, Z_DVAL_P(op1) + Z_DVAL_P(op2));
            ZEND_VM_NEXT_OPCODE();
        } else if (EXPECTED(Z_TYPE_INFO_P(op2) == IS_LONG)) {
            result = EX_VAR(opline->result.var);
            ZVAL_DOUBLE(result, Z_DVAL_P(op1) + ((double)Z_LVAL_P(op2)));
            ZEND_VM_NEXT_OPCODE();
        }
    }

    SAVE_OPLINE();
    if (OP1_TYPE == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(op1) == IS_UNDEF)) {
        op1 = GET_OP1_UNDEF_CV(op1, BP_VAR_R);
    }
    if (OP2_TYPE == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(op2) == IS_UNDEF)) {
        op2 = GET_OP2_UNDEF_CV(op2, BP_VAR_R);
    }
    add_function(EX_VAR(opline->result.var), op1, op2);
    FREE_OP1();
    FREE_OP2();
    ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}

Ohne zu verstehen, was dieser Code tut, können Sie sehen, dass er ziemlich komplex ist.

Das Ziel wäre also das Weglassen ZEND_RECV komplett und ersetzen ZEND_ADD mit ZEND_ADD_INT_INT die keine Überprüfung (über das Bewachen hinaus) oder Verzweigen durchführen muss, da die Arten von Parametern bekannt sind.

Um diese wegzulassen und a ZEND_ADD_INT_INT Sie müssen in der Lage sein, zuverlässig auf die Typen von zu schließen $a und $b zur Kompilierzeit. Der Rückschluss auf die Kompilierzeit ist manchmal einfach, z. $a und $b sind ganze Zahlen oder Konstanten.

Buchstäblich gesternPHP 7.1 hat etwas wirklich Ähnliches: Es gibt jetzt typspezifische Handler für einige Hochfrequenz-Opcodes wie z ZEND_ADD. Opcache kann den Typ einiger Variablen ableiten, in einigen Fällen sogar die Typen von Variablen innerhalb eines Arrays ableiten und generierte Opcodes ändern, um den Normalen zu verwenden ZEND_ADDum einen typspezifischen Handler zu verwenden:

ZEND_VM_TYPE_SPEC_HANDLER(ZEND_ADD, (res_info == MAY_BE_LONG && op1_info == MAY_BE_LONG && op2_info == MAY_BE_LONG), ZEND_ADD_LONG_NO_OVERFLOW, CONST|TMPVARCV, CONST|TMPVARCV, SPEC(NO_CONST_CONST,COMMUTATIVE))
{
    USE_OPLINE
    zval *op1, *op2, *result;

    op1 = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R);
    op2 = GET_OP2_ZVAL_PTR_UNDEF(BP_VAR_R);
    result = EX_VAR(opline->result.var);
    ZVAL_LONG(result, Z_LVAL_P(op1) + Z_LVAL_P(op2));
    ZEND_VM_NEXT_OPCODE();
}

Nochmals, ohne zu verstehen, was irgendetwas davon bewirkt, können Sie sagen, dass dies der Fall ist viel einfacher auszuführen.

Diese Optimierungen sind sehr cool, aber die effektivsten und interessantesten Optimierungen werden kommen, wenn PHP ein JIT hat.

  • Um genau zu sein, verschlechtert es die Leistung sogar um einen sehr kleinen … sagen wir vernachlässigbaren Betrag 😉 Es sind ein paar Kontrollen weniger, aber nichts wirklich Auffälliges.

    – bwoebi

    5. Oktober 2015 um 8:19 Uhr

  • Das ist technisch richtig. Aber Verbesserung oder Verschlechterung sollten nicht der entscheidende Faktor dafür sein, ob Sie eine solche Funktion verwenden (oder nicht).

    – Joe Watkins

    5. Oktober 2015 um 9:29 Uhr

  • @JoeWatkins ist dies ab PHP 8.1 immer noch der Fall?

    – Erich

    12. Juli um 15:51 Uhr

Benutzer-Avatar
Federkun

Gibt es Leistungsvorteile durch die Verwendung dieser Funktionen? Wenn ja, wie?

Nicht noch.

Dies ist jedoch der erste Schritt für eine effizientere Generierung von Opcodes. Entsprechend RFC: Scalar Type Hints Zukünftiger Umfang:

Da skalare Typhinweise garantieren, dass ein übergebenes Argument innerhalb eines Funktionskörpers (zumindest anfänglich) von einem bestimmten Typ ist, könnte dies in der Zend Engine für Optimierungen verwendet werden. Wenn beispielsweise eine Funktion zwei Float-hinted-Argumente nimmt und mit ihnen arithmetisch arbeitet, besteht für die arithmetischen Operatoren keine Notwendigkeit, die Typen ihrer Operanden zu überprüfen.

In früheren Versionen von PHP gab es keine Möglichkeit zu wissen, welche Art von Parameter an eine Funktion übergeben werden konnte, was es wirklich schwierig macht, einen JIT-Kompilierungsansatz zu haben, um eine überlegene Leistung zu erzielen, wie bei Facebook HHVM tun.

@ircmaxell in seinem bloggen erwähnt die Möglichkeit, all dies mit nativer Kompilierung auf die nächste Stufe zu bringen, was sogar noch besser wäre als JIT.

Aus Sicht der Leistung öffnen skalare Hinweise die Türen für die Implementierung dieser Optimierungen. Aber an und für sich keine Leistungssteigerung.

1228400cookie-checkSind skalare und strenge Typen in PHP7 eine leistungssteigernde Funktion?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy