Maison >développement back-end >tutoriel php >Outil d'optimisation des performances PHP : générateur

Outil d'optimisation des performances PHP : générateur

藏色散人
藏色散人avant
2020-01-21 11:58:592264parcourir

Outil d'optimisation des performances PHP : générateur

Si vous travaillez en Python ou dans d'autres langages, vous devez être familier avec les générateurs. Cependant, de nombreux développeurs PHP peuvent ne pas connaître la fonctionnalité des générateurs, soit parce que les générateurs ont été introduits dans PHP 5.5.0, soit parce que leur fonctionnalité n'est pas évidente. Mais la fonction générateur est vraiment utile.

Avantages

Si je parle directement du concept, je pense que vous serez toujours confus après l'avoir écouté, alors parlons d'abord des avantages, peut-être que cela peut susciter votre intérêt. Alors, quels sont les avantages des générateurs, comme suit :

● Les générateurs auront un grand impact sur les performances des applications PHP

● Économisez beaucoup de mémoire lorsque le code PHP est en cours d'exécution

● Plus adapté au calcul de grandes quantités de données

Alors, comment ces fonctions magiques sont-elles réalisées ? Donnons d'abord un exemple.

Introduction au concept

Tout d'abord, éliminons le fardeau du concept du générateur et examinons une simple fonction PHP :

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

C'est une fonction PHP très courante, nous l'utilisons souvent lors du traitement de certains tableaux. Le code ici est également très simple :

1. On crée une fonction.

2. La fonction contient une boucle for. Nous bouclons l'heure actuelle dans $data

3 Une fois la boucle for exécutée, $data est renvoyée.

Ce n’est pas encore fini, continuons. Écrivons une autre fonction et imprimons la valeur de retour de cette fonction dans une boucle :

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

Jetons un coup d'œil aux résultats exécutés dans le navigateur :

Outil doptimisation des performances PHP : générateur

C'est très parfait, aucun problème. (Bien sûr, vous ne pouvez pas voir l'effet du sommeil(1))

Pensez à une question

Nous avons remarqué que lors de l'appel de la fonction createRange, la valeur passée à $number est 10, un très petit nombre. Supposons que nous transmettions maintenant une valeur de 1 000 000 (10 millions).

Ensuite, dans la fonction createRange, la boucle for doit être exécutée 10 millions de fois. Et 10 millions de valeurs sont placées dans $data, et le tableau $data est placé en mémoire. Par conséquent, beaucoup de mémoire sera occupée lors de l’appel de fonctions.

Ici, le générateur peut entrer en jeu.

Créer un générateur

On modifie le code directement, merci de faire attention :

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

Regardez ce code qui est très similaire à tout à l'heure, nous supprimez-le Array $data, et n'a rien renvoyé, mais a utilisé un mot-clé rendement

avant time() en utilisant le générateur

Exécutons-le à nouveau Le deuxième morceau de code :

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

Outil doptimisation des performances PHP : générateur

Nous avons miraculeusement découvert que la valeur de sortie est différente de la première fois sans utiliser le générateur. Les valeurs (horodatages) ici sont séparées de 1 seconde.

L'intervalle d'une seconde ici est en fait le résultat du sommeil(1). Mais pourquoi n’y a-t-il pas d’écart la première fois ? En effet :

● Lorsque le générateur n'est pas utilisé : le résultat de la boucle for dans la fonction createRange est rapidement placé dans $data et renvoyé immédiatement. Par conséquent, la boucle foreach est un tableau fixe.

● Lors de l'utilisation d'un générateur : la valeur de createRange n'est pas générée rapidement d'un coup, mais dépend de la boucle foreach. foreach boucle une fois et for est exécuté une fois.

À ce stade, vous devriez avoir une idée des générateurs.

Compréhension approfondie du générateur

Analyse du code

Analysons le code tout à l'heure.

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

Restaurons le processus d'exécution du code.

1. Appelez d'abord la fonction createRange, en passant le paramètre 10, mais la valeur for est exécutée une fois puis s'arrête, et indique à foreach la valeur qui peut être utilisée pour la première boucle.

2. foreach commence à boucler sur $result, d'abord sleep(1), puis commence à utiliser une valeur donnée par for pour effectuer la sortie.

3.foreach prépare la deuxième boucle Avant de démarrer la deuxième boucle, il demande à nouveau la boucle for.

4. La boucle for est exécutée à nouveau, indiquant à foreach l'horodatage généré

5 foreach obtient la deuxième valeur et la génère. En raison de sleep(1) dans foreach, la boucle for est retardée d'une seconde pour générer l'heure actuelle

Par conséquent, pendant toute l'exécution du code, il n'y a toujours qu'une seule valeur d'enregistrement participant à la boucle, et. il n'y a qu'une seule information dans la mémoire.

Peu importe la taille du $number initialement transmis, puisque tous les jeux de résultats ne sont pas générés immédiatement, la mémoire est toujours une boucle de valeurs.

Compréhension conceptuelle

À ce stade, vous devriez avoir une compréhension approximative de ce qu'est un générateur. Parlons ci-dessous du principe du générateur.

Tout d'abord, clarifions un concept : le mot-clé rendement du générateur n'est pas une valeur de retour. Son terme professionnel est appelé valeur de sortie.

Alors qu'est-ce que c'est. la boucle foreach dans le code ? En effet, lorsque PHP utilise un générateur, il renverra un objet de la classe Generator. foreach peut itérer l'objet. Pour chaque itération, PHP calculera la valeur qui doit être itérée ensuite via l'instance du générateur. De cette façon, foreach connaîtra la valeur qui doit être itérée ensuite.

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

实际开发应用

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

读取超大文件

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

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

Outil doptimisation des performances PHP : générateur

我们创建一个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;;
}

Outil doptimisation des performances PHP : générateur

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

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

这样,即使读取上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

 打印日志

Outil doptimisation des performances PHP : générateur

 CPU和内存消耗

Outil doptimisation des performances PHP : générateur

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

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer