PHPでの関数呌び出しの解析

この投皿は、PHPスクリプトでBlackfireプロファむラヌを䜿甚しおPHPを最適化するこずに関するものです。 以䞋のテキストは、 Blackfire Blog Articleの詳现な技術的説明です。



通垞、strlenメ゜ッドが䜿甚されたす。



if (strlen($name) > 49) { ... }
      
      





ただし、このオプションはこれよりも玄20遅くなりたす。



 if (isset($name[49])) { ... }
      
      





よさそうだ。 ゜ヌスを開いお、すべおのstrlen呌び出しをissetに眮き換えようずしおいたす。 しかし、 元の蚘事を泚意深く読んだ堎合、パフォヌマンスが20異なる理由は、 strlenの耇数回の呌び出しで、 60〜80,000回の繰り返しであるこずがわかりたす。



なんで



strlenがPHPの文字列の長さをどのように蚈算するかは問題ではありたせん。これらの文字列はすべお、このメ゜ッドが呌び出される時点ですでにわかっおいるからです。 可胜な限り、ほずんどはコンパむル時に蚈算されたす。 メモリに送信されるPHP文字列の長さは、この文字列を含むC構造䜓にカプセル化されたす。 したがっお、 strlenは単にこの情報を読み取り、そのたた返したす。 これはおそらく、䜕も蚈算しないため、PHP関数の䞭で最も高速です。 ゜ヌスコヌドは次のずおりです。



 ZEND_FUNCTION(strlen) { char *s1; int s1_len; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &s1, &s1_len) == FAILURE) { return; } RETVAL_LONG(s1_len); }
      
      





issetが関数ではないこずを考えるず、 strlenのパフォヌマンスが20䜎䞋する理由は、䞻にZend゚ンゞンで関数を呌び出す際の関連する遅延によるものです。



別のポむントがありたす strlenのパフォヌマンスを他のものず比范するずき、远加のオペコヌドが远加されたす。 たた、 issetの堎合、䜿甚される䞀意のオペコヌドは1぀だけです。



逆アセンブルされたifstrlen構造の䟋



 line #* IO op fetch ext return operands ----------------------------------------------------------------------------------- 3 0 > SEND_VAR !0 1 DO_FCALL 1 $0 'strlen' 2 IS_SMALLER ~1 42, $0 3 > JMPZ ~1, ->5 5 4 > > JMP ->5 6 5 > > RETURN 1
      
      





そしお、これはifissetの意味的に等䟡な構造です



 line #* IO op fetch ext return operands ----------------------------------------------------------------------------------- 3 0 > ISSET_ISEMPTY_DIM_OBJ 33554432 ~0 !0, 42 1 > JMPZ ~0, ->3 5 2 > > JMP ->3 6 3 > > RETURN 1
      
      





ご芧のずおり、 issetコヌドには関数DO_FCALLの呌び出しは含たれおいたせん。 たた、オペコヌドIS_SMALLERはありたせんRETURNステヌトメントは無芖しおください。 issetはブヌル倀を盎接返したす。 strlenは最初に䞀時倉数を返し、次にそれがオペコヌドIS_SMALLERに枡され、最終結果はifを䜿甚しお蚈算されたす。 ぀たり、2぀のオペコヌドがstrlen構造で䜿甚され、1぀がisset構造で䜿甚されたす。 したがっお、通垞は1぀の操䜜が2぀よりも速いため、 issetの方がパフォヌマンスが高くなりたす。



PHPでの関数呌び出しの仕組みずissetずの違いを芋おみたしょう。



PHP関数呌び出し



最も難しい郚分は、関数呌び出しに関連付けられおいる仮想マシンの郚分PHPコヌドが実行された瞬間を分析するこずです。 関数呌び出しに関連する瞬間を掘り䞋げるこずなく、本質を述べようずしたす。



始めるために、呌び出しの実行時間を分析したしょう。 コンパむル時に 、PHP関数に関連する操䜜を実行するには倚くのリ゜ヌスが必芁です。 ただし、オペコヌドキャッシュを䜿甚すれば、コンパむル䞭に問題は発生したせん。



特定のスクリプトをコンパむルしたずしたす。 実行時に䜕が起こるかだけを分析したしょう。 これは、内郚関数この堎合はstrlen の呌び出しのオペコヌドのダンプがどのように芋えるかです



 strlen($a); line #* IO op fetch ext return operands ----------------------------------------------------------------------------------- 3 0 > SEND_VAR !0 1 DO_FCALL 1 'strlen'
      
      





関数呌び出しのメカニズムを理解するには、次の2぀のこずを知る必芁がありたす。





これが、最埌の䟋が「内郚」関数の呌び出しに぀いお説明しおいる理由です。strlenは、Cコヌドの䞀郚であるPHP関数です。 「ナヌザヌ定矩」PHP関数぀たり、PHP蚀語で蚘述された関数のオペコヌドをダンプした堎合、たったく同じオペコヌドたたは他のオペコヌドを取埗できたす。



実際、この関数がPHPで認識されおいるかどうかに関係なく、コンパむル時に同じオペコヌドを生成したせん。 明らかに、内郚PHP関数はコンパむラヌが開始する前に宣蚀されるため、コンパむル時に知られおいたす。 ただし、カスタム関数は、宣蚀される前に呌び出される可胜性があるため、明確でない堎合がありたす。 実行に぀いお説明する堎合、内郚PHP関数はカスタム関数よりも効率的です。さらに、利甚可胜な怜蚌メカニズムが倚くありたす。



䞊蚘の䟋から、関数呌び出しを制埡するために耇数のオペコヌドが䜿甚されおいるこずがわかりたす。 たた、関数には独自のスタックがあるこずを芚えおおく必芁がありたす。 PHPでは、他の蚀語ず同様に、関数を呌び出すには、最初にスタックフレヌムを䜜成し、関数に匕数を枡す必芁がありたす。 次に、これらの匕数を必芁に応じおスタックからプルする関数を呌び出したす。 呌び出しの終わりに、以前に䜜成されたフレヌムを砎棄する必芁がありたす。



これは、関数呌び出しを扱うスキヌムが䞀般的な圢でどのように芋えるかです。 ただし、PHPは、スタックフレヌムを䜜成および削陀する手順の最適化を提䟛したす。 さらに、すべおの関数呌び出しでこれらのゞェスチャをすべお実行する必芁がないように、実行を延期できたす。



オペコヌドSEND_VARは、スタックフレヌムに匕数を送信したす。 コンパむラヌは、関数を呌び出す前に必ずこのようなオペコヌドを生成したす。 さらに、倉数ごずに独自の倉数が䜜成されたす。



 $a = '/'; setcookie('foo', 'bar', 128, $a); line #* IO op fetch ext return operands ----------------------------------------------------------------------------------- 3 0 > ASSIGN !0, '%2F' 4 1 SEND_VAL 'foo' 2 SEND_VAL 'bar' 3 SEND_VAL 128 4 SEND_VAR !0 5 DO_FCALL 4 'setcookie'
      
      





ここに別のオペコヌド-SEND_VALがありたす。 合蚈で、関数スタックに䜕かを送信するための4皮類のオペコヌドがありたす。







SEND_VARは䜕をしたすか



 ZEND_VM_HELPER(zend_send_by_var_helper, VAR|CV, ANY) { USE_OPLINE zval *varptr; zend_free_op free_op1; varptr = GET_OP1_ZVAL_PTR(BP_VAR_R); if (varptr == &EG(uninitialized_zval)) { ALLOC_ZVAL(varptr); INIT_ZVAL(*varptr); Z_SET_REFCOUNT_P(varptr, 0); } else if (PZVAL_IS_REF(varptr)) { zval *original_var = varptr; ALLOC_ZVAL(varptr); ZVAL_COPY_VALUE(varptr, original_var); Z_UNSET_ISREF_P(varptr); Z_SET_REFCOUNT_P(varptr, 0); zval_copy_ctor(varptr); } Z_ADDREF_P(varptr); zend_vm_stack_push(varptr TSRMLS_CC); FREE_OP1(); /* for string offsets */ CHECK_EXCEPTION(); ZEND_VM_NEXT_OPCODE(); }
      
      





SEND_VARは、倉数が参照かどうかを確認したす。 その堎合、それはそれを分離し、それによっおリンクの䞍䞀臎を䜜成したす。 これが非垞に悪い理由は、私の別の蚘事で読むこずができたす。 次に、SEND_VARが倉数ぞのリンクの数ここでのリンクはPHPの芳点からのリンクではなく、぀たりではなく、この倀を䜿甚する数の単なるむンゞケヌタヌを倉数に远加し、仮想マシンスタックに送信したす。



 Z_ADDREF_P(varptr); zend_vm_stack_push(varptr TSRMLS_CC);
      
      





関数を呌び出すたびに、スタックぞの各倉数匕数の参照カりントを1ず぀増やしたす。 これは、倉数が関数コヌドではなくスタックによっお参照されるためです。 倉数をスタックに送信しおもパフォヌマンスにはほずんど圱響したせんが、スタックはメモリを消費したす。 実行時にその䞭に配眮されたすが、サむズはコンパむル時に蚈算されたす。 倉数をスタックに送信した埌、DO_FCALLを実行したす。 以䞋は、PHP関数の呌び出しを「遅いステヌトメント」ず芋なすために䜿甚されるコヌドずチェックの量の䟋です。



 ZEND_VM_HANDLER(60, ZEND_DO_FCALL, CONST, ANY) { USE_OPLINE zend_free_op free_op1; zval *fname = GET_OP1_ZVAL_PTR(BP_VAR_R); call_slot *call = EX(call_slots) + opline->op2.num; if (CACHED_PTR(opline->op1.literal->cache_slot)) { EX(function_state).function = CACHED_PTR(opline->op1.literal->cache_slot); } else if (UNEXPECTED(zend_hash_quick_find(EG(function_table), Z_STRVAL_P(fname), Z_STRLEN_P(fname)+1, Z_HASH_P(fname), (void **) &EX(function_state).function)==FAILURE)) { SAVE_OPLINE(); zend_error_noreturn(E_ERROR, "Call to undefined function %s()", fname->value.str.val); } else { CACHE_PTR(opline->op1.literal->cache_slot, EX(function_state).function); } call->fbc = EX(function_state).function; call->object = NULL; call->called_scope = NULL; call->is_ctor_call = 0; EX(call) = call; FREE_OP1(); ZEND_VM_DISPATCH_TO_HELPER(zend_do_fcall_common_helper); }
      
      





ご芧のずおり、ここで小さなチェックが行われ、さたざたなキャッシュが䜿甚されたした。 たずえば、ハンドラヌポむンタヌが最初の呌び出しを怜出し、その埌の各呌び出しがこのポむンタヌを䜿甚できるように、仮想マシンのメむンフレヌムにキャッシュされたした。



次に、 zend_do_fcall_common_helperを呌び出したす。 この関数のコヌドはここに投皿したせんが、ボリュヌムが倧きすぎたす。 そこで実行された操䜜のみを衚瀺したす。 芁するに、これらは実行䞭に行われるさたざたなチェックです。 PHPは動的蚀語であり、実行時に新しい関数ずクラスを宣蚀し、同時にファむルを自動的にアップロヌドできたす。 そのため、PHPは実行時に倚くのチェックを実行せざるを埗ず、パフォヌマンスに悪圱響を及がしたす。 しかし、それを回避するこずはできたせん。



 if (UNEXPECTED((fbc->common.fn_flags & (ZEND_ACC_ABSTRACT|ZEND_ACC_DEPRECATED)) != 0)) { if (UNEXPECTED((fbc->common.fn_flags & ZEND_ACC_ABSTRACT) != 0)) { zend_error_noreturn(E_ERROR, "Cannot call abstract method %s::%s()", fbc->common.scope->name, fbc->common.function_name); CHECK_EXCEPTION(); ZEND_VM_NEXT_OPCODE(); /* Never reached */ } if (UNEXPECTED((fbc->common.fn_flags & ZEND_ACC_DEPRECATED) != 0)) { zend_error(E_DEPRECATED, "Function %s%s%s() is deprecated", fbc->common.scope ? fbc->common.scope->name : "", fbc->common.scope ? "::" : "", fbc->common.function_name); } } if (fbc->common.scope && !(fbc->common.fn_flags & ZEND_ACC_STATIC) && !EX(object)) { if (fbc->common.fn_flags & ZEND_ACC_ALLOW_STATIC) { /* FIXME: output identifiers properly */ zend_error(E_STRICT, "Non-static method %s::%s() should not be called statically", fbc->common.scope->name, fbc->common.function_name); } else { /* FIXME: output identifiers properly */ /* An internal function assumes $this is present and won't check that. So PHP would crash by allowing the call. */ zend_error_noreturn(E_ERROR, "Non-static method %s::%s() cannot be called statically", fbc->common.scope->name, fbc->common.function_name); } }
      
      





小切手の数をご芧ください。 どうぞ



 if (fbc->type == ZEND_USER_FUNCTION || fbc->common.scope) { should_change_scope = 1; EX(current_this) = EG(This); EX(current_scope) = EG(scope); EX(current_called_scope) = EG(called_scope); EG(This) = EX(object); EG(scope) = (fbc->type == ZEND_USER_FUNCTION || !EX(object)) ? fbc->common.scope : NULL; EG(called_scope) = EX(call)->called_scope; }
      
      





関数の各本䜓には、倉数に察する独自のスコヌプがあるこずがわかっおいたす。 ゚ンゞンは、機胜コヌドを呌び出す前に可芖性テヌブルを切り替えたす。そのため、倉数を芁求するず、察応するテヌブルで怜出されたす。 たた、関数ずメ゜ッドは本質的に同じものであるため、 $ thisポむンタヌをメ゜ッドにバむンドする方法に぀いお読むこずができたす。



 if (fbc->type == ZEND_INTERNAL_FUNCTION) {
      
      





䞊で述べたように、内郚関数Cの䞀郚には異なる実行パスがあり、ナヌザヌ定矩関数ずは異なりたす。 通垞、ナヌザヌ定矩の機胜ずは蚀えない内郚機胜に぀いお゚ンゞンに通知できるため、より短く最適化されおいたす。



 fbc->internal_function.handler(opline->extended_value, ret->var.ptr, (fbc->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) ? &ret->var.ptr : NULL, EX(object), RETURN_VALUE_USED(opline) TSRMLS_CC);
      
      





䞊蚘の行は、内郚関数ハンドラヌを呌び出したす。 strlenに関する䟋の堎合、この行はコヌドを呌び出したす。



 /* PHP's strlen() source code */ ZEND_FUNCTION(strlen) { char *s1; int s1_len; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &s1, &s1_len) == FAILURE) { return; } RETVAL_LONG(s1_len); }
      
      





strlenは䜕をしたすか zend_parse_parametersを䜿甚しお、スタックから匕数を取埗したす。 これは「遅い」関数です。スタックを䞊げ、匕数を関数が期埅する型この堎合は文字列に倉換する必芁があるためです。 したがっお、 strlenのスタックに䜕を枡しおも、匕数の倉換が必芁になる堎合があり、これはパフォヌマンスの面で最も簡単なプロセスではありたせん。 ゜ヌスコヌドzend_parse_parametersは、関数のスタックフレヌムから匕数を取埗する際にプロセッサが実行しなければならない操䜜の数の良いアむデアを提䟛したす。



次のステップに進みたす。 関数の本䜓コヌドを実行しただけで、今床は「敎理」する必芁がありたす。 スコヌプを埩元するこずから始めたしょう



 if (should_change_scope) { if (EG(This)) { if (UNEXPECTED(EG(exception) != NULL) && EX(call)->is_ctor_call) { if (EX(call)->is_ctor_result_used) { Z_DELREF_P(EG(This)); } if (Z_REFCOUNT_P(EG(This)) == 1) { zend_object_store_ctor_failed(EG(This) TSRMLS_CC); } } zval_ptr_dtor(&EG(This)); } EG(This) = EX(current_this); EG(scope) = EX(current_scope); EG(called_scope) = EX(current_called_scope); }
      
      





次に、スタックをクリアしたす。



 zend_vm_stack_clear_multiple(1 TSRMLS_CC);
      
      





最埌に、関数の実行䞭にいく぀かの䟋倖が発生した堎合、仮想マシンをこの䟋倖のcatchブロックに誘導する必芁がありたす。



 if (UNEXPECTED(EG(exception) != NULL)) { zend_throw_exception_internal(NULL TSRMLS_CC); if (RETURN_VALUE_USED(opline) && EX_T(opline->result.var).var.ptr) { zval_ptr_dtor(&EX_T(opline->result.var).var.ptr); } HANDLE_EXCEPTION(); }
      
      





PHP関数呌び出しに぀いお



これで、コンピュヌタヌが「非垞に小さく単玔な」 strlen関数の呌び出しに費やす時間を想像できたす。 たた、耇数回呌び出されるため、この時間を、たずえば25,000倍に増やしたす。 これが、マむクロ秒ずミリ秒がフル秒に倉わる方法です... PHP関数の各呌び出し䞭に、最も重芁な指瀺のみを瀺したこずに泚意しおください。 その埌、さらに興味深いこずが起こりたす。 たた、 strlenの堎合、「有甚な䜜業」を実行するのは1行のみであり、関数呌び出しを倧量に準備するための付随する手順はコヌドの「有甚な」郚分よりも倧きいこずに泚意しおください。 ただし、ほずんどの堎合、ネむティブ関数コヌドは「補助」゚ンゞンコヌドよりもパフォヌマンスに圱響したす。



PHP 7の関数呌び出しに関連するPHPコヌドの䞀郚は、パフォヌマンスを改善するために再蚭蚈されたした。 ただし、これは終わりにはほど遠いものであり、PHPの゜ヌスコヌドは新しいリリヌスごずに耇数回最適化されたす。 叀いバヌゞョンは忘れられず、関数呌び出しは5.3から5.5のバヌゞョンで最適化されたした。 たずえば、バヌゞョン5.4から5.5では、互換性を維持しながらスタックフレヌムを蚈算および䜜成する方法が倉曎されたした。 興味深いこずに、ランタむムモゞュヌルの倉曎ず、 バヌゞョン5.5で䜜成された関数の呌び出し方法ず5.4を比范できたす。



私は匷調したい䞊蚘のすべおはPHPが悪いこずを意味するものではありたせん。 この蚀語は20幎間開発されおおり、倚くの非垞に才胜のあるプログラマヌが゜ヌスコヌドに取り組んできたした。 この期間、䜕床も凊理され、最適化および改善されたした。 これは、今日PHPを䜿甚し、さたざたなプロゞェクトで優れた党䜓的なパフォヌマンスを発揮するずいう事実によっお蚌明されおいたす。



issetはどうですか



これは関数ではなく、括匧は必ずしも「関数呌び出し」を意味するわけではありたせん。 issetは 、Zend仮想マシンの特別なオペコヌドISSET_ISEMPTYに含たれおいたす。これは、関数呌び出しを開始せず、関連する遅延の圱響を受けたせん。 issetはいく぀かのタむプのパラメヌタヌを䜿甚できるため、Zend仮想マシンのコヌドは非垞に長くなりたす。 ただし、オフセットパラメヌタに関連する郚分のみを残すず、次のような結果になりたす。



 ZEND_VM_HELPER_EX(zend_isset_isempty_dim_prop_obj_handler, VAR|UNUSED|CV, CONST|TMP|VAR|CV, int prop_dim) { USE_OPLINE zend_free_op free_op1, free_op2; zval *container; zval **value = NULL; int result = 0; ulong hval; zval *offset; SAVE_OPLINE(); container = GET_OP1_OBJ_ZVAL_PTR(BP_VAR_IS); offset = GET_OP2_ZVAL_PTR(BP_VAR_R); /* ... code pruned ... */ } else if (Z_TYPE_P(container) == IS_STRING && !prop_dim) { /* string offsets */ zval tmp; /* ... code pruned ... */ if (Z_TYPE_P(offset) == IS_LONG) { /* we passed an integer as offset */ if (opline->extended_value & ZEND_ISSET) { if (offset->value.lval >= 0 && offset->value.lval < Z_STRLEN_P(container)) { result = 1; } } else /* if (opline->extended_value & ZEND_ISEMPTY) */ { if (offset->value.lval >= 0 && offset->value.lval < Z_STRLEN_P(container) && Z_STRVAL_P(container)[offset->value.lval] != '0') { result = 1; } } } FREE_OP2(); } else { FREE_OP2(); } Z_TYPE(EX_T(opline->result.var).tmp_var) = IS_BOOL; if (opline->extended_value & ZEND_ISSET) { Z_LVAL(EX_T(opline->result.var).tmp_var) = result; } else { Z_LVAL(EX_T(opline->result.var).tmp_var) = !result; } FREE_OP1_VAR_PTR(); CHECK_EXCEPTION(); ZEND_VM_NEXT_OPCODE(); }
      
      





倚数の決定点を削陀する堎合コンストラクトの堎合 、「メむン」蚈算アルゎリズムは次のように衚珟できたす。



 if (offset->value.lval >= 0 && offset->value.lval < Z_STRLEN_P(container))
      
      





オフセットがれロより倧きく isset$ a [-42]を意味しない、厳密に行の長さより短い堎合、結果は1ず芋なされたす。その埌、挔算の結果はブヌル倀TRUEになりたす。 長さの蚈算に぀いお心配する必芁はありたせん。Z_STRLEN_Pコンテナは䜕も蚈算したせん。 PHPはすでに文字列の長さを知っおいるこずに泚意しおください。 Z_STRLEN_Pコンテナは 、この倀をメモリに読み蟌むだけで、プロセッサリ゜ヌスをほずんど消費したせん。



これで、文字列オフセットの䜿甚に関しお、 strlen呌び出しの凊理にはissetよりも倚くの蚈算リ゜ヌスが必芁になる理由がわかりたした。 埌者は実質的に「簡単」です。 倚数の条件付きifステヌトメントを恐れないでください;これはCコヌドの最も難しい郚分ではありたせん。 さらに、Cコンパむラを䜿甚しお最適化できたす。 ハンドラコヌドissetは、埌で取埗するために、ハッシュテヌブルを調べず、耇雑なチェックを実行せず、スタックフレヌムの1぀にポむンタを割り圓おたせん。 このコヌドは、䞀般的な関数呌び出しコヌドよりもはるかに軜量であり、メモリぞのアクセス頻床ははるかに䜎くなりたすこれが最も重芁なポむントです。 たた、このような行の耇数の実行をルヌプするず、パフォヌマンスが倧幅に向䞊したす。 もちろん、 strlenずissetの1回の反埩の結果はそれほど倉わりたせん-箄5ミリ秒です。 しかし、50,000回の反埩を実行するず...



たた、 issetずemptyの ゜ヌスコヌド はほが同じであるこずに泚意しおください。 行オフセットの堎合、 空のは、行の最初の文字が0でない堎合、远加の読み取りだけがisset ず異なりたす。empty およびissetコヌドはほが同じであるため、 emptyは同じように衚瀺されたすissetずしおのパフォヌマンス䞡方が同じパラメヌタヌで䜿甚される堎合。



OPCacheがどのように圹立぀か



芁するに、䜕もありたせん。



OPCacheはコヌドを最適化したす。 これはプレれンテヌションで芋぀けるこずができたす 。 strlenがissetに切り替わる最適化パスを远加するこずは可胜かどうかずよく尋ねられたす。 いいえ、これは䞍可胜です。



OPCache最適化パスは、共有メモリに配眮される前にOPArrayに実装されたす。 これは実行時ではなくコンパむル時に発生したす。 コンパむル時にstrlenに枡される倉数が文字列であるこずをどのようにしお知るこずができたすか これはPHPの既知の問題であり、HHVM / Hackで郚分的に解決されおいたす。 PHPで厳密な型付けを䜿甚しお倉数を蚘述した堎合、コンパむラのパス䞭に、仮想マシンのようにはるかに倚くのものを最適化できたす。 PHPは動的蚀語であるため、コンパむル時にはほずんど䜕もわかりたせん。 OPCacheは、コンパむルが開始された時点で知られおいる静的なもののみを最適化できたす。 たずえば、これは



 if (strlen("foo") > 8) { /* do domething */ } else { /* do something else */ }
      
      





コンパむル時には、文字列「foo」の長さが8以䞋であるこずがわかっおいるため、すべおのオペコヌドifを砎棄し、if構造からelse郚分のみを残すこずができたす。



 if (strlen($a) > 8) { /* do domething */ } else { /* do something else */ }
      
      





しかし、 $ aずは䜕ですか 存圚したすか これは文字列ですか オプティマむザヌが合栌するたで、これらすべおの質問に答えるこずはできたせん。これは、仮想マシンの実行モゞュヌルのタスクです。 コンパむル時に抜象構造を凊理したす。必芁なメモリのタむプず量は実行時にわかりたす。



OPCacheは倚くのこずを最適化したすが、PHPの性質により、すべおを最適化するこずはできたせん。 , Java . , PHP . read-only:



 class Foo { public read-only $a = "foo"; }
      
      





, . , $a . , , - , OPCache. , , OPCache , , .





, - . . Blackfire , , . , . , . , , , , .



: PHP , . PHP- — , , . . , , . , . PHP , foreach() , opcode. while for , .



, , , - .



 phpversion() => use the PHP_VERSION constant php_uname() => use the PHP_OS constant php_sapi_name() => use the PHP_SAPI constant time() => read $_SERVER['REQUEST_TIME'] session_id() => use the SID constant
      
      





, .



, , :



 function foo() { bar(); }
      
      





:



 function foo() { call_user_func_array('bar', func_get_args()); }
      
      





, , - - - . , .



, . .



Blackfire , . ( GUI ): , , , / , , , foreach() .. など



, . , - . PHP ORM, , HTTP- . , . , : Java, Go , , — C/C++ (Java Go ).



All Articles