2015-10-05 21 views
29

Od PHP7 możemy teraz use scalar typehint and ask for strict types on a per-file basis. Czy korzystanie z tych funkcji przynosi jakieś korzyści? Jeśli tak, w jaki sposób?Czy typy skalarne i ścisłe w PHP7 to funkcja zwiększająca wydajność?

Około interwebs Mam tylko znaleźć koncepcyjne korzyści, takie jak:

  • bardziej precyzyjnych błędów
  • uniknięcie problemów z niechcianymi typu przymus
  • więcej kod semantyczny, unikając nieporozumień podczas korzystania nawzajem kod
  • lepsza ocena IDE kodu
+3

Jednym potencjalnie poprawiającym wydajność efektem podpowiedzi typu skalarnego jest to, że typ rzucany jest zmuszany do wystąpienia wcześniej, co * może * zmniejszyć liczbę kolejnych rzutów. – NikiC

Odpowiedz

32

Dzisiaj użycie skalarnego i prążkowego typy ct w PHP7 nie zwiększają wydajności.

PHP7 nie ma kompilatora JIT.

Jeśli jakiś czas w przyszłości PHP uzyska kompilator JIT, nie jest zbyt trudno wyobrazić sobie optymalizacje, które mogłyby zostać wykonane z dodatkowymi informacjami o typie.

Jeśli chodzi o optymalizacje bez JIT, typy skalarne są tylko częściowo pomocne.

Weźmy następujący kod:

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

Jest to kod wygenerowany przez Zend na to:

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 jest kod operacji, które wykonuje wpisać weryfikacji i przymus do otrzymanych parametrów. Następnego opcode jest 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(); 
} 

Bez zrozumienia tego, co każdy z tego kodu nie można zobaczyć, że jest to dość skomplikowane.

Więc celem byłoby pominięcie ZEND_RECV całkowicie, i zastąpienie ZEND_ADD z ZEND_ADD_INT_INT który nie musi wykonywać żadnych kontroli (poza pilnuje) lub rozgałęzienia, ponieważ znane są rodzaje params.

Aby je pominąć i mieć numer ZEND_ADD_INT_INT, należy rzetelnie wywnioskować typy $a i $b podczas kompilacji. Wnioskowanie na temat kompilacji jest czasami łatwe, na przykład: $a i $b są liczbami całkowitymi lub stałymi.

Dosłownie yesterday, PHP 7.1 ma coś bardzo podobnego: Istnieją teraz specyficzne dla danego typu programy obsługi dla niektórych opcodów wysokiej częstotliwości, takich jak ZEND_ADD. Opcache jest w stanie wywnioskować, rodzaj niektórych zmiennych, to nawet w stanie wywnioskować typy zmiennych w tablicy w niektórych przypadkach i zmienić opcodes generowane używać normalnego ZEND_ADD, do korzystania z konkretnego programu obsługi typu:

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(); 
} 

Ponownie, nie rozumiejąc, co to może zrobić, można stwierdzić, że jest to łatwiejsze do wykonania.

Te optymalizacje są bardzo fajne, jednak najbardziej efektywne i najciekawsze optymalizacje pojawią się, gdy PHP ma JIT.

+4

Mówiąc dokładniej, nawet obniża wydajność o bardzo mały ... powiedzmy znikomy rozmiar ;-) Jest to kilka czeków mniej, ale nic naprawdę zauważalnego. – bwoebi

+2

Jest to prawdą techniczną. Jednak wzmocnienie lub degradacja nie powinny decydować o tym, czy używasz (lub nie) takiej funkcji. –

14

Czy są jakieś korzyści związane z wydajnością korzystania z tych funkcji? Jeśli tak, w jaki sposób?

Nie jeszcze.

Jest to jednak pierwszy krok do wydajniejszego generowania kodów opcode. Według RFC:Future Scope skalarne Rodzaj wskazówki męska:

Ponieważ skalarne podpowiedzi typu gwarantuje, że przeszedł argumentem będzie pewien rodzaj wewnątrz ciała funkcji (przynajmniej początkowo), to może być stosowane w Zend Engine do optymalizacji. Na przykład, jeśli funkcja pobiera dwa argumenty z podpowiedziami pływającymi i wykonuje operacje arytmetyczne z , operatory arytmetyczne nie muszą sprawdzać typów ich operandów.

W poprzedniej wersji php nie było sposobu, aby wiedzieć, jaki rodzaj parametru może zostać przekazany do funkcji, która sprawia, że ​​naprawdę trudno mieć podejście kompilację JIT, aby osiągnąć wysoką wydajność, jak Facebooka HHVM zrobić.

@ircmaxell w swoim numerze blog wspomina o możliwości przeniesienia tego wszystkiego na wyższy poziom dzięki natywnej kompilacji, która byłaby nawet lepsza niż JIT.

Z punktu widzenia wydajności, wskazówki skalarne otwierają drzwi do wdrożenia tych optymalizacji. Ale nie zwiększa wydajności sam w sobie.