SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

DeveloperZine(デベロッパージン)- エンジニアの意思決定を支える技術情報メディア ProductZine

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

Yahoo! JAPAN 黒帯シリーズ

PHP 7のパフォーマンスが高い理由

Yahoo! JAPAN 黒帯シリーズ 第1回 ~ 「PHPコア黒帯」による最新PHP 7の解説


関数の引数の処理

 関数の引数の処理についても速くなっています。関数の解析に使われている関数zend_parse_parameters()がCPUの5%を占めていました。これは、zend_parse_parameters()の引数に与えている、その関数の引数仕様を理解するために、scanf()のように文字列を解析しているからです。具体的には内部で以下のようなコードになっています。

if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,
    "za|b",
    &value,
    &array,
    &strict) == FAILURE) {
  return;
}

 PHP 7になって、互換性を保つために、この関数も残されていますが、パフォーマンスが高いマクロも作られました。前述のコードは以下のように書き換えられます。

ZEND_PARSE_PARAMETERS_START()
  Z_PARAM_ZVAL(value)
  Z_PARAM_ARRAY(array)
  Z_PARAM_OPTIONAL
  Z_PARAM_BOOL(strict)
ZEND_PARSE_PARAMETERS_END();

 これだけではイメージできないので、中をみていきましょう。zend_parse_parameters()はこのようになっています。

……

for (spec_walk = type_spec; *spec_walk; spec_walk++) {
  c = *spec_walk;
  switch (c) {
      case 'l': case 'd':
      case 's': case 'b':
      case 'r': case 'a':
      case 'o': case 'O':
      case 'z': case 'Z':
      case 'C': case 'h':
      case 'f': case 'A':
      case 'H': case 'p':
      case 'S': case 'P':
      case 'L':
        max_num_args++;
        break;
      case '|':
        min_num_args = max_num_args;
        break;
      case '/':
      case '!':
        /* Pass */
        break;
      case '*':
      case '+':

        ……

      default:

        ……

  }
}

……

 以上のように、文字を1文字ずつ読んでいき、解析しながら進めています。少しずつの積み重ねでも、関数を呼ぶたびに解析していくので負荷が高まっていくでしょう。一方、マクロで書くと以下のようになります。

……
do { \
  if (UNEXPECTED(_num_args < _min_num_args) || \
      (UNEXPECTED(_num_args > _max_num_args) && \
      EXPECTED(_max_num_args >= 0))) { \
    if (!(_flags & ZEND_PARSE_PARAMS_QUIET)) { \
      zend_wrong_paramers_count_error(_num_args, _min_num_args, _max_num_args); \
    } \
    error_code = ZPP_ERROR_FAILURE; \
    break; \
  } \
  _i = 0; \
  _real_arg = ZEND_CALL_ARG(execute_data, 0);

  ……

  if (separate) { \
    Z_PARAM_PROLOGUE(separate); \
    zend_parse_arg_zval_deref(_arg, &dest, check_null); \
  } else { \
    if (UNEXPECTED(++_i >_num_args)) break; \
    _real_arg++; \
    zend_parse_arg_zval(_real_arg, &dest, check_null); \
  }

   ……

  if (UNEXPECTED(!zend_parse_arg_array(_arg, &dest, check_null, 0))) { \
    _expected_type = Z_EXPECTED_ARRAY; \
    error_code = ZPP_ERROR_WRONG_ARG; \
    break; \
  }

  _optional = 1;

  ……

  } while (0); \
  if (UNEXPECTED(error_code != ZPP_ERROR_OK)) { \
    if (!(_flags & ZEND_PARSE_PARAMS_QUIET)) { \
      if (error_code == ZPP_ERROR_WRONG_CALLBACK) { \
        zend_wrong_callback_error(E_WARNING, _i, _error); \
      } else if (error_code == ZPP_ERROR_WRONG_CLASS) { \
        zend_wrong_paramer_class_error(_i, _error, _arg); \
      } else if (error_code == ZPP_ERROR_WRONG_ARG) { \
        zend_wrong_paramer_type_error(_i, _expected_type, _arg); \
      } \
    } \
    failure; \
  } \
} while (0)

……

 行末に\が付いているのは、マクロなので1行に収める必要があるからです。do while文になっていますが、条件式が0となっているのでループしません。では、なぜdo while文になっているかというと、途中でbreakして抜け出せるようにするためです。引数の型を順番に取得していっているだけなので、zend_parse_parameters()と比べるとだいぶシンプルになっています。文字列を解析していくわけではなく、決められた順番で決められた型を処理していくので、処理が早くなっているのです。

まとめ

 zval、ハッシュテーブル、関数の引数の処理とみてきましたが、PHP 5とPHP 7は機構が違ってきていることが分かります。メモリを使った方がよいところはメモリを使っていますが、全体的に無駄はなくなっているように思います。個人的には、PHP 5のzvalは、すべての型に対して同じような構造を有しており、美しいと思っていたので好きでした。PHP 7では、その美しさを捨てて、型を差別化することによって最適化して速くなったと言えるでしょう。

修正履歴

この記事は参考になりましたか?

連載通知を行うには会員登録(無料)が必要です。
既に会員の方はを行ってください。
Yahoo! JAPAN 黒帯シリーズ連載記事一覧

もっと読む

この記事の著者

蒋池 東龍(ヤフー株式会社)(コモイケ トウリュウ)

ヤフー株式会社 COO 事業推進本部 プロダクト開発 黒帯 PHPコア1999年セガ(当時セガ・エンタープライゼス)に新卒入社しプログラマーとして「ジェットセットラジオ」「Jリーグ スペクタクルサッカー」「J.LEAGUEプロサッカークラブをつくろう!」の開発に従事。2004年10月ヤフー株式会社入...

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/8492 2015/05/15 10:43

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー