12

私は比較的 PHP に不慣れで、言語固有の特異性をゆっくりと学んでいます。私がよく悩まされることの 1 つは、私が (そう言われているように) あまりにも多くの関数呼び出しを使用し、一般的にそれらを回避するために何かをするように求められることです。以下に 2 つの例を示します。

// Change this:
} catch (Exception $e) {
  print "It seems that error " . $e->getCode() . " occured";
  log("Error: " . $e->getCode());
}

// To this:
} catch (Exception $e) {
  $code = $e->getCode();
  print "It seems that error " . $code . " occured";
  log("Error: " . $code);
}

2番目の例

// Change this:
$customer->setProducts($products);

// To this:
if (!empty($products)) {
  $customer->setProducts($products);
}

最初の例では、広告にわずかな認知オーバーヘッドを割り当てる$e->getCode()ことがわかりました。$code「'$code' とは何ですか? ああ、それは例外からのコードです。」一方、2 番目の例では、循環的な複雑さが追加されます。どちらの例でも、最適化によって読みやすさと保守性が犠牲になっていることがわかりました。

パフォーマンスの向上はそれだけの価値がありますか、それともこのマイクロ最適化ですか?

私たちは今のところ PHP 5.2 で立ち往生していることに注意してください。

非常に大まかなベンチ テストを行ったところ、ベンチ テストの性質に応じて、関数呼び出しのパフォーマンス ヒットが 10% から 70% 程度になることがわかりました。これが重要であることは認めます。しかし、catch ブロックがヒットする前に、データベースと HTTP エンドポイントへの呼び出しがありました。$productsが設定される前に$customer、配列に複雑な並べ替えが発生しました$products結局のところ、この最適化は、コードの読み取りと保守を難しくするコストを正当化するものでしょうか? または、これらの例は単純化されていますが、2 番目の例が最初の例と同じくらい簡単または読みやすいと思う人はいますか (私はウィーナーです)。

これに関する良い記事や研究を引用できる人はいますか?

編集:

ベンチテストの例:

<?php
class Foo {
        private $list;
        public function setList($list) {
                $this->list = $list;
        }
}

$foo1 = new Foo();

for($i = 0; $i < 1000000; $i++) {
        $a = array();
        if (!empty($a))
                $foo1->setList($a);
}
?>

コマンドでそのファイルを実行しますtime。ある特定のマシンでは、数回実行した後、平均で 0.60 秒かかりました。if (!empty($a))実行に平均 3.00 秒かかる原因をコメントアウトします。

明確化: これらは例です。最初の例は、ドメイン固有ではない単純な例を犠牲にして、ひどい例外処理と DRY 違反の可能性を示しています。

4

4 に答える 4

8

PHP 関数呼び出しのオーバーヘッドは、正確に 15.5355% です。

:) 鍋をかき混ぜるだけです。

真剣に、ここに主題に関するいくつかの素晴らしいリンクがあります:

PHP アプリケーションにあまりにも多くの関数を含めることは可能ですか?

関数と繰り返しコード

これらのリンクでのコードの保守性と速度の議論は、OP によって暗示される (おそらくもっと重要な) 質問に対処しますが、将来このスレッドに出くわす人々に関連し、うまくいけば役立つかもしれない小さなデータを追加するだけです。 2011 Macbook Pro (ドライブ容量が非常に少なく、実行中のプログラムが多すぎる) で以下のコードを実行した結果です。

他の場所で述べたように、関数を呼び出すかコードを「インライン」にするかを決定する際の重要な考慮事項は、特定のコード ブロック内から関数が呼び出される回数です。関数が呼び出される回数が多いほど、インラインで作業を行うことを検討する価値があります。

結果 (秒単位の時間)

Call Function メソッド | インライン方式 | 違い | 異なる割合

1,000回の反復(4回の実行)

0.0039088726043701 | 0.0031478404998779 | 0.00076103210449219 | 19.4694

0.0038208961486816 | 0.0025999546051025 | 0.0012209415435791 | 31.9543

0.0030159950256348 | 0.0029480457305908 | 6.7949295043945E-5 | 2.2530

0.0031449794769287 | 0.0031390190124512 | 5.9604644775391E-6 | 0.1895

1,000,000 回の反復 (4 回の実行)

3.1843111515045 | 2.6896121501923 | 0.49469900131226 | 15.5355

3.131945848465 | 2.7114839553833 | 0.42046189308167 | 13.4249

3.0256152153015 | 2.7648048400879 | 0.26081037521362 | 8.6201

3.1251409053802 | 2.7397727966309 | 0.38536810874939 | 12.3312

function postgres_friendly_number($dirtyString) {
    
    $cleanString = str_ireplace("(", "-", $dirtyString);
    $badChars = array("$", ",", ")");
    $cleanString = str_ireplace($badChars, "", $cleanString);
    
    return $cleanString;
    
}


//main
$badNumberString = '-$590,832.61';

$iterations = 1000000;

$startTime = microtime(true);
for ($i = 1; $i <= $iterations; $i++) {
    $goodNumberString = postgres_friendly_number($badNumberString);
}
$endTime = microtime(true);
$firstTime = ($endTime - $startTime); 

$startTime = microtime(true);
for ($i = 1; $i <= $iterations; $i++) {
    $goodNumberString = str_ireplace("(", "-", $badNumberString);
    $badChars = array("$", ",", ")");
    $goodNumberString = str_ireplace($badChars, "", $goodNumberString);
}
$endTime = microtime(true); 
$secondTime = ($endTime - $startTime); 

$timeDifference = $firstTime - $secondTime;
$percentDifference = (( $timeDifference / $firstTime ) * 100);
于 2014-02-10T18:21:46.410 に答える
8

サーバーのハードウェアが関数呼び出しのオーバーヘッドにどのように関連しているかについては、まだ誰も議論していません。

関数が呼び出されると、CPU のすべてのレジスタに現在の実行ポイントに関連するデータが含まれます。CPU のすべてのレジスタをメモリ (通常はプロセスのスタック) に保存する必要があります。そうしないと、その実行ポイントに戻って実行を再開することができなくなります。関数から戻るとき、すべての CPU のレジスタをメモリ (通常はプロセスのスタック) から復元する必要があります。

したがって、ネストされた関数呼び出しの文字列がプロセスにオーバーヘッドを追加する方法を見ることができます。CPU のレジスタはスタックに何度も保存し、関数から戻るために何度も復元する必要があります。

これは、実際には関数呼び出しのオーバーヘッドの原因です。また、関数の引数が渡される場合、関数を呼び出す前にそれらをすべて複製する必要があります。したがって、巨大な配列を関数の引数として渡すことは、設計として適切ではありません。

ゲッター/セッターの使用のオーバーヘッドについて、オブジェクト指向 PHP で調査が行われました。すべてのゲッター/セッターを削除すると、実行時間が約 50% 短縮されます。そして、それは単に関数呼び出しのオーバーヘッドによるものです。

于 2014-11-18T21:40:12.053 に答える
5

標準的な PHP の実装は、実装が簡単で、PHP が目的とするアプリケーションが高速な関数呼び出しのような生のパフォーマンスを必要としないため、非常に低速です。

他のPHP 実装を検討することもできます。

PHP で作成する必要があるアプリケーションを作成している場合 (DB からネットワーク経由でブラウザーにデータをダンプする)、関数呼び出しのオーバーヘッドは重要ではありません。関数を使用するとオーバーヘッドが大きくなりすぎるのではないかと恐れているため、わざわざコードを複製しないでください。

于 2013-07-31T13:51:18.237 に答える