ホームページ >バックエンド開発 >PHPチュートリアル >PHP パフォーマンス最適化ツール: ジェネレーター

PHP パフォーマンス最適化ツール: ジェネレーター

藏色散人
藏色散人転載
2020-01-21 11:58:592288ブラウズ

PHP パフォーマンス最適化ツール: ジェネレーター

Python または他の言語で作業している場合は、ジェネレーターについてよく知っている必要があります。ただし、ジェネレーターは PHP 5.5.0 で導入されたか、その機能が明確ではないため、多くの PHP 開発者はジェネレーターの機能を知らない可能性があります。しかし、ジェネレーター機能は本当に便利です。

利点

コンセプトについて直接話すと、聞いてもまだ混乱すると思いますので、最初に利点について話しましょう。あなたの興味を呼び起こします。では、ジェネレーターの利点は次のとおりです。

#●● ジェネレーターは PHP アプリケーションのパフォーマンスに大きな影響を与えます

● PHP コードの実行時に大量のメモリを節約します

● 大量のデータの計算により適しています

では、これらの魔法の機能はどのように実現されるのでしょうか?まず例を挙げてみましょう。

概念の紹介

まず、ジェネレーターの概念の重荷を脇に置いて、単純な PHP 関数を見てみましょう:

function createRange($number){
    $data = [];
    for($i=0;$i<$number;$i++){
        $data[] = time();
    }
    return $data;
}

これは、非常に一般的な PHP 関数で、配列を処理するときによく使用されます。ここのコードも非常に単純です:

1. 関数を作成します。

2. 関数には for ループが含まれています。ループ内の $data に現在時刻を入力します。

3. for ループが実行されると、$data が返されます。

まだ終わっていないので、続けましょう。別の関数を作成し、この関数の戻り値をループで出力してみましょう:

$result = createRange(10); // 这里调用上面我们创建的函数
foreach($result as $value){
    sleep(1);//这里停顿1秒,我们后续有用
    echo $value.&#39;<br />&#39;;
}

ブラウザで実行結果を見てみましょう:

PHP パフォーマンス最適化ツール: ジェネレーター# #これは非常に完璧で、何の問題もありません。 (もちろん、sleep(1) の効果はわかりません)

質問について考えてください

関数 createRange を呼び出すときに、値が渡されることに気付きました。 to $number は 10 で、非常に小さい数です。ここで、10000000 (1000 万) の値を渡すとします。

次に、関数 createRange では、for ループを 1,000 万回実行する必要があります。そして、1,000 万個の値が $data に配置され、$data 配列がメモリに配置されます。したがって、関数を呼び出すときに多くのメモリが占​​有されます。

ここでジェネレーターがその才能を発揮します。

ジェネレーターを作成します

コードを直接変更します。注意してください:

function createRange($number){
    for($i=0;$i<$number;$i++){
        yield time();
    }
}

今のコードとよく似ています。削除します 配列 $data が返され、何も返されません。代わりに、キーワード yield が time() の前に使用されます。

ジェネレーターを使用します。

もう一度実行してみましょう2 番目のコード:

$result = createRange(10); // 这里调用上面我们创建的函数
foreach($result as $value){
    sleep(1);
    echo $value.&#39;<br />&#39;;
}

PHP パフォーマンス最適化ツール: ジェネレータージェネレーターを使用しない場合、出力値が最初と異なることが奇跡的に発見されました。ここでの値(タイムスタンプ)は 1 秒ごとに区切られています。

ここでの 1 秒間隔は、実際には sleep(1) の結果です。しかし、なぜ最初はギャップがないのでしょうか?その理由は次のとおりです。

# ジェネレーターが使用されていない場合: createRange 関数の for ループの結果はすぐに $data に配置され、すぐに返されます。したがって、foreach ループは固定配列になります。

# ジェネレーターを使用する場合: createRange の値は一度にすぐに生成されるのではなく、foreach ループに依存します。 foreach は 1 回ループし、for は 1 回実行されます。

この時点で、ジェネレーターについてある程度理解できたはずです。

ジェネレーターについての深い理解

コード分析

コードを分析してみましょう。

function createRange($number){
    for($i=0;$i<$number;$i++){
        yield time();
    }
}
 
$result = createRange(10); // 这里调用上面我们创建的函数
foreach($result as $value){
    sleep(1);
    echo $value.&#39;<br />&#39;;
}

コード実行プロセスを復元しましょう。

1. まず createRange 関数を呼び出してパラメータ 10 を渡しますが、for 値は 1 回実行されてから停止し、最初のループで使用できる値を foreach に伝えます。

2. foreach は $result のループを開始し、最初に sleep(1) を実行し、次に for で指定された値を使用して出力を実行し始めます。

3.foreach は 2 番目のループを準備し、2 番目のループを開始する前に、再度 for ループを要求します。

4. for ループが再度実行され、生成されたタイムスタンプが foreach に指示されます。

5. foreach は 2 番目の値を取得して出力します。 foreach の sleep(1) により、for ループは現在時刻を生成するのに 1 秒遅れます

したがって、コード実行全体で、ループに参加するレコード値は常に 1 つだけです。記憶の中の情報の一つに過ぎません。

最初に渡される $number がどれほど大きくても、すべての結果セットがすぐに生成されるわけではないため、メモリは常に値のループになります。

概念的な理解

この時点で、ジェネレーターとは何かについて大まかに理解できるはずです。以下でジェネレータの原理について説明しましょう。

まず、概念を明確にします。ジェネレーターの yield キーワードは戻り値ではありません。専門用語では出力値と呼ばれます。値を生成するだけです。

それでは、何ですか。コード内の foreach ループ?実際、PHP がジェネレーターを使用すると、Generator クラスのオブジェクトが返されます。 foreach はオブジェクトを反復できます。反復ごとに、PHP は次に Generator インスタンスを通じて反復する必要がある値を計算します。このようにして、foreach は次に反復する必要がある値を認識します。

而且,在运行中for循环执行后,会立即停止。等待foreach下次循环时候再次和for索要下次的值的时候,for循环才会再执行一次,然后立即再次停止。直到不满足条件不执行结束。

实际开发应用

很多PHP开发者不了解生成器,其实主要是不了解应用领域。那么,生成器在实际开发中有哪些应用?

读取超大文件

PHP开发很多时候都要读取大文件,比如csv文件、text文件,或者一些日志文件。这些文件如果很大,比如5个G。这时,直接一次性把所有的内容读取到内存中计算不太现实。

这里生成器就可以派上用场啦。简单看个例子:读取text文件

PHP パフォーマンス最適化ツール: ジェネレーター

我们创建一个text文本文档,并在其中输入几行文字,示范读取。

<?php
header("content-type:text/html;charset=utf-8");
function readTxt()
{
    # code...
    $handle = fopen("./test.txt", &#39;rb&#39;);
    while (feof($handle)===false) {
        # code...
        yield fgets($handle);
    }
    fclose($handle);
}
 
foreach (readTxt() as $key => $value) {
    # code...
    echo $value.&#39;<br />&#39;;
}

PHP パフォーマンス最適化ツール: ジェネレーター

通过上图的输出结果我们可以看出代码完全正常。

但是,背后的代码执行规则却一点儿也不一样。使用生成器读取文件,第一次读取了第一行,第二次读取了第二行,以此类推,每次被加载到内存中的文字只有一行,大大的减小了内存的使用。

这样,即使读取上G的文本也不用担心,完全可以像读取很小文件一样编写代码。

批量更新数据库表字段

/**
 * @desc: 方法描述
 * @param int $count 数组个数(需要循环多少次)
 * @param int $limit 数组大小
 * @return \Generator
 */
public function getAddressContent($count = 1, $limit = 20000)
{
    for ($i = 0; $i < ceil($count / $limit); $i++) {
        $result = StudentModel::where(&#39;id&#39;,&#39;<&#39;,&#39;67265&#39;)
            ->limit($i * $limit, $limit)
            ->order(&#39;id desc&#39;)
            ->select()->toArray();
        yield $result;
    }
}
 
/**
 * @desc: 修改数据库 省份、城市
 * @throws Exception
 */
public function idCard()
{
    $count = 200000000; // 需要更新的数据
    foreach ($this->getAddressContent($count) as $key=>$lists) {
        foreach ($lists as $k => $v) {
            $peopleIdentity = new Identity($v[&#39;idcard&#39;]);
            $peopleRegion = $peopleIdentity->region();
            if($peopleRegion->code() != 0 ){
                $res = StudentModel::where(&#39;id&#39;, $v[&#39;id&#39;])->update([
                    &#39;birthday&#39; => $peopleIdentity->birthday()??&#39;&#39;,
                    &#39;province&#39; => $peopleRegion->province()??&#39;&#39;,
                    &#39;city&#39; => $peopleRegion->city()??&#39;&#39;,
                    &#39;county&#39; => $peopleRegion->county()??&#39;&#39;,
                ]);
                Log::debug(&#39;更新结果 [&#39; . $v[&#39;id&#39;] . &#39;]: &#39; . json_encode($res));
            }
        }
    }
    echo "success";
}

使用命令行执行

php id_card.php

 打印日志

PHP パフォーマンス最適化ツール: ジェネレーター

 CPU和内存消耗

PHP パフォーマンス最適化ツール: ジェネレーター

更多php知识,请访问php教程

以上がPHP パフォーマンス最適化ツール: ジェネレーターの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はcnblogs.comで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。