PHP Iterator,yield,range创建大数字迭代器对比.

range是一个生成数组函数,可以生成一个可遍历的数字数组.如range(1,5)返回[1,2,3,4,5]

对比结果

生成100万数据,耗时和内存消耗对比,使用php8.1.23版本

创建:

只创建. $arr=range(1,1000000);,$arr=new IRange(1,1000000),xrange(1,1000000)
方法内存耗时
内置range函数32.3964MB0.01714s
Iterator类401.9609kB0.00005s
Yield 生成器402.5078kB0.00006s

结论: 看出来Iterator和Yield 内存和效率都差不多.range函数会占大量内存.

遍历:

使用foreach 来遍历
方法内存耗时
内置range函数32.3972MB0.01968s
Iterator类402.7734kB0.24452s
Yield 生成器403.0156kB0.05788s

结论:Iterator遍历最慢,Yield和range速度差不多.

综合,当有大量数据需要遍历时,使用生器创建.

Range函数

$arr= range(1,1000000,1);
foreach ($arr as $key=>$value){}

Iterator类

class IRange   implements  Iterator {
    private int $point=0;
    private int $value=0;
    private int $start=0;
    private int $end=0;
    private int $step=1;
    public function __construct(int $start,int $end,int $step=1)
    {
        $this->start=$start;
        $this->value=$start;
        $this->end=$end;
        $this->step=$step;
    }
    public function current(): int
    {
        return $this->value;
    }
    public function next():void{
        $this->point++;
        $this->value += $this->step;
    }
    public function key():int{
        return $this->point;
    }
    public function valid():bool{
        return ($this->value >= $this->start && $this->value <= $this->end);
    }
    public function rewind():void{
        $this->point=0;
        $this->value=$this->start;
    }
}

// 创建
$arr=new IRange(1,1000000);
foreach ($arr as $key=>$value){}

Yield 生成器

function xRange($start,$end,$step=1): Generator
{
    for($key=0,$value=$start;$value<=$end;$key++,$value=$value+$step){
        yield $key=>$value;
    }
}
$arr= xrange(1,1000000);

内存和耗时函数

php查看运行内存和耗时代码.

内存函数:

// 代码
memory_get_usage();// 当前使用内存
memory_get_peak_usage(true);// 最大真实内存

耗时:

当前时间-开时时间

$startTime=microtime(true);
// 代码
$endTime=microtime(true);
echo number_format($endTime-$startTime,5,'.','');

Trace类代码参考:

<?php declare(strict_types=1);

class Trace{

    private static float $startTime=0;
    private static float $endTime=0;
    private static int $tag=0;

    public static function Start(){
        self::$startTime=microtime(true);
        self::$endTime=microtime(true);
        echo "\033[2;37m";
        printf("php_ver:%s,max_mem:%s",PHP_VERSION,ini_get("memory_limit"));
        echo "\e[0m ".PHP_EOL;
    }

    private static function out(float $size,float $time,string $msg=''){
        self::$tag++;
        $unit=array('b','kB','MB','GB','TB','PB');
        $mem=@number_format($size/pow(1024,($i=floor(log($size,1024)))),4,'.','').''.$unit[$i];
        $time=@number_format($time,5,'.','');
        echo "\033[2;37m";
        printf("[%s] mem:%s,time:%ss", self::$tag,$mem,$time);
        echo "\e[0m ".$msg.PHP_EOL;

    }

    public static function Log(string $msg=''){
        $endTime=microtime(true);
        $time=$endTime-self::$endTime;
        self::$endTime=$endTime;
        self::out(memory_get_usage(),$time,"[$msg]");
    }

    public static function End(){
        $endTime=microtime(true);
        $time=$endTime-self::$startTime;
        self::$endTime=$endTime;
        self::out(memory_get_peak_usage(true),$time,'[end]');
    }

    public static function run(callable $callback,int $num=1,$out=false){
        self::Start();
        if(!$out) {
            ob_start(function(){return null;},2);
        }

        while($num-->0) {
            $callback();
        }
        if(ob_get_status()) {
            ob_end_clean();
        }

        self::End();
    }
}

使用:

Trace::Start();
// 代码1
Trace::Log("测试1");
// 代码2
Treace::Log("测试2");
Trace::End();
// 循环测试print_r函数100次,不输出内容.
Trace::run(function(){
  print_r("hello");
},100,false);

测试

<?php declare(strict_types=1);

require "./trace.php";

// 自定义迭代器
class IRange   implements  Iterator {
    private int $point=0;
    private int $value=0;
    private int $start=0;
    private int $end=0;
    private int $step=1;
    public function __construct(int $start,int $end,int $step=1)
    {
        $this->start=$start;
        $this->value=$start;
        $this->end=$end;
        $this->step=$step;
    }

    public function current(): int
    {
        return $this->value;
    }
    public function next():void{
        $this->point++;
        $this->value += $this->step;
    }
    public function key():int{
        return $this->point;
    }
    public function valid():bool{
        return ($this->value >= $this->start && $this->value <= $this->end);
    }
    public function rewind():void{
        $this->point=0;
        $this->value=$this->start;
    }

}



function xRange($start,$end,$step=1): Generator
{
    for($key=0,$value=$start;$value<=$end;$key++,$value=$value+$step){
        yield $key=>$value;
    }
}

Trace::Start();

// 通过迭代器
//$arr=new IRange(1,1000000,1);
//Trace::Log("iterator");

// 通过数组
//$arr= range(1,1000000,1);
//Trace::Log("range");

// 通过yield
$arr= xrange(1,1000000,1);
Trace::Log("yield");

foreach ($arr as $key=>$value){}

Trace::Log("foreach");
Trace::End();

结果:

➜  php iterator.php
php_ver:8.1.23,max_mem:128M 
[1] mem:402.4063kB,time:0.00005s [iterator]
[2] mem:402.7734kB,time:0.24452s [foreach]
[3] mem:2.0000MB,time:0.24463s [end]
➜  php iterator.php
php_ver:8.1.23,max_mem:128M 
[1] mem:32.3968MB,time:0.01456s [range]
[2] mem:32.3972MB,time:0.01968s [foreach]
[3] mem:34.0039MB,time:0.03428s [end]
➜  php iterator.php
php_ver:8.1.23,max_mem:128M 
[1] mem:402.9609kB,time:0.00010s [yield]
[2] mem:403.0156kB,time:0.05788s [foreach]
[3] mem:2.0000MB,time:0.05802s [end]

cpu用时也可以用linux的time命令查看.

\time  php iterator.php
后续 : Yield 和 Fiber 的使用

原作者:阿金
本文地址:https://hi-arkin.com/archives/iterator-yield-range.html

标签: php yield iterator range

(本篇完)

评论