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
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_ADD
um 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.
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.
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