PHP 内存优化:使用 Yield 生成器来善待你的内存

分类: 开发语言 > PHP

你有没有想过在 PHP 中有 yield 的好处是什么?我有一些相关的要点想介绍给你,以节省 googling 的时间:

  1. 什么是 yield.
  2. yield 与 return 之间的差异.
  3. yield 的相关使用设置.
  4. 结论.
  5. 参考.

1. 什么是 yield

生成器函数看起来和普通函数一样,只是生成器不返回值,而是生成所需的值。

看看以下示例:

function getValues() {
    yield 'value';
}// 打印字符串 "value"
echo getValues();

当然,这不是它的工作方式,前面的例子会给你一个致命的错误: Object Of class Generator could not be convert to string,让我们弄清楚:

2. yield 与 return 的不同

前面的错误意味着 getValues() 函数没有按预期返回字符串,让我们检查它的类型:

function getValues() {
    return 'value';
}
var_dump(getValues()); // string(5) "value"
function getValues() {
    yield 'value';
}
var_dump(getValues()); // class 
Generator#1 (0) {}

Generator 类实现了 Iterator 接口,这意味着您必须循环使用 getValue() 函数才能获取值:

foreach (getValues() as $value) {
   echo $value;
}
// using variable is also alright
$values = getValues();
foreach ($values as $value) {
   echo $value;
}

但这不是唯一的区别!

生成器允许您编写使用 foreach 的代码对一组数据进行迭代的代码,而无需在内存中构建数组,那样可能会导致超出内存限制。

在下面的例子里我们创建一个有 800,000 元素的数字同时从 getValues() 方法中返回他,同时在此期间,我们将使用函数 memory_get_usage() 来获取分配给次脚本的内存, 我们将会每增加 200,000 个元素来获取一下内存使用量,这意味着我们将会提出四个检查点:

<?php
function getValues() {
   $valuesArray = [];
   // 获取初始内存使用量
   echo round(memory_get_usage() / 1024 / 1024, 2) . ' MB' . PHP_EOL;
   for ($i = 1; $i < 800000; $i++) {
      $valuesArray[] = $i;
      // 为了让我们能进行分析,所以我们测量一下内存使用量
      if (($i % 200000) == 0) {
         // 来 MB 为单位获取内存使用量
         echo round(memory_get_usage() / 1024 / 1024, 2) . ' MB'. PHP_EOL;
      }
   }
   return $valuesArray;
}
$myValues = getValues(); // 一旦我们调用函数将会在这里创建数组
foreach ($myValues as $value) {}

在前面的示例中 输出结果为内存消耗,脚本的输出结果如下

0.34 MB
8.35 MB
16.35 MB
32.35 MB

这意味着我们这几行脚本消耗了超过 30M 的内存,每次我们向 $valuesArray 数组添加元素,都会增加它在内存中的大小:

举个使用 yield 的例子:

<?php
function getValues() {
   // 获取初始内存使用
   echo round(memory_get_usage() / 1024 / 1024, 2) . ' MB' . PHP_EOL;
   for ($i = 1; $i < 800000; $i++) {
      yield $i;

      // 开始进行分析 测量内存占用
      if (($i % 200000) == 0) {
         // get memory usage in megabytes
         echo round(memory_get_usage() / 1024 / 1024, 2) . ' MB'. PHP_EOL;
      }

   }
}

$myValues = getValues(); // 遍历完毕`values`之前不进行任何操作
foreach ($myValues as $value) {} // 此处开始获取`values`

这个脚本的输出令人惊讶:

0.34 MB
0.34 MB
0.34 MB
0.34 MB

这不意味着你从 return 表达式迁移到 yield, 但如果你在应用中创建会导致服务器上内存出问题的巨大数组,则 yield 更加适合你的情况。

3. 什么是 “yield” 选项

这里有很多 yield 的选项,我需要强调其中的几个:

a. 使用 yield, 你也可以使用 return:

function getValues() {
   yield 'value';
   return 'returnValue';
}
$values = getValues();
foreach ($values as $value) {}
echo $values->getReturn(); // 'returnValue'

b. 返回键值对:

function getValues() {
   yield 'key' => 'value';
}
$values = getValues();
foreach ($values as $key => $value) {
   echo $key . ' => ' . $value;
}

4. 结论

这个主题的主要原因是为了明确 yield 和 return 特别是在内存使用方面的区别,使用一些例子是因为我发现他对任何开发人员思考真的很重要。

来源:https://mp.weixin.qq.com/s/teYt56EE_rW3406VVdrQhA 发布时间:2022-06-30 12:33:28