PHP的yield:一个以时间换空间的方法

今天在闲逛PHP的官方文档的时候,发现了这么个用法:yield,叫 生成器
官方是这么描述的

(PHP 5 >= 5.5.0, PHP 7)
生成器提供了一种更容易的方法来实现简单的对象迭代,相比较定义类实现 Iterator 接口的方式,性能开销和复杂性大大降低。
生成器允许你在 foreach 代码块中写代码来迭代一组数据而不需要在内存中创建一个数组, 那会使你的内存达到上限,或者会占据可观的处理时间。相反,你可以写一个生成器函数,就像一个普通的自定义函数一样, 和普通函数只返回一次不同的是, 生成器可以根据需要 yield 多次,以便生成需要迭代的值。
一个简单的例子就是使用生成器来重新实现 range() 函数。 标准的 range() 函数需要在内存中生成一个数组包含每一个在它范围内的值,然后返回该数组, 结果就是会产生多个很大的数组。 比如,调用 range(0, 1000000) 将导致内存占用超过 100 MB。
做为一种替代方法, 我们可以实现一个 xrange() 生成器, 只需要足够的内存来创建 Iterator 对象并在内部跟踪生成器的当前状态,这样只需要不到1K字节的内存。

果然还是完全看不懂在说什么。看下官方的示例代码吧:

/*
* for the protection from the leaking of resources
* see RFC https://wiki.php.net/rfc/generators#closing_a_generator
* and use finnaly
*/

//sample code

function getLines($file) {
    $f = fopen($file, 'r');
    try {
        while ($line = fgets($f)) {
            yield $line;
        }
    } finally {
        fclose($f);
    }
}

foreach (getLines("file.txt") as $n => $line) {
    if ($n > 5) break;
    echo $line;
}

简而言之呢,就是如果在某个函数中使用了生成器yield那么这个这个函数会被推迟到实际被foreach语句迭代的时候才会被调用。
官方的描述是会大量节省内存,提高内存。我心想这是神器啊!但是转念一想,如果真的这么好用,为啥却没有被大规模使用呢?是出反常必有妖。

下面测试一下:

传统写法如下:

function test()
{
   $data = [];
   for ($i = 0; $i < 1000000; $i++) {
	   //echo "执行了一次";
	   $data[] = $i;
   }
}
foreach (test() as $value) {
    var_dump($value);
    //echo "<br>";
}

然后看运行时间和内存占用是:

  • 运行时间为:0.0513
  • 消耗内存为:0.7968KB

使用生成器的写法:

function test()
{
   for ($i = 0; $i < 1000000; $i++) {
	   //echo "执行了一次";
	   yield $i;
   }
}
foreach (test() as $value) {
    var_dump($value);
    //echo "<br>";
}

再看下运行的时间与消耗的内存:

  • 运行时间为:0.5249
  • 消耗内存为:0.1093KB

emmmm… 传统写法因为会一次性先把数据存入内存,所以占用内存比较大。而yield的写法,则可以有效节约内存。但是相对应的代价就是:时间消耗大约是传统写法的十倍

看来yield只适合处理较大数据例如数G的表格之类的,明显对内存造成了过大的压力情况下才去使用。在数据量比较小的情况下,还是不要使用了。至于背后的原理,则不是本身讨论的内容了(我不会说是因为我也不知道)。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值