在 PHP 中使用 `yield` 來(lái)做內(nèi)存優(yōu)化

image.png

你有沒(méi)有想過(guò) "在 PHP 中使用 yield 會(huì)有什么益處"琼讽,我將為你節(jié)省一些谷歌搜索的時(shí)間; 我列出了一些要向你介紹的要點(diǎn)來(lái)全面認(rèn)知 yield

  1. 什么是 yield
  2. yield & return 的區(qū)別。
  3. yield 有什么選項(xiàng)。
  4. 結(jié)論焚辅。
  5. 參考。

1. 什么是 "yield"

生成器函數(shù)看上去就像一個(gè)普通函數(shù)硕并, 除了不是返回一個(gè)值之外法焰, 生成器會(huì)根據(jù)需求產(chǎn)生更多的值。

來(lái)看以下的例子:

function getValues() {
    yield 'value';
}

// 輸出字符串 "value"
echo getValues();

當(dāng)然倔毙, 這不是他生效的方式埃仪, 前面的例子會(huì)給你一個(gè)致命的錯(cuò)誤: 類生成器的對(duì)象不能被轉(zhuǎn)換成字符串, 讓我們清楚的說(shuō)明:

2. "yield" & "return" 的區(qū)別

前面的錯(cuò)誤意味著 getValues() 方法不會(huì)如預(yù)期返回一個(gè)字符串陕赃,讓我們檢查一下他的類型:

function getValues() {
    return 'value';
}
var_dump(getValues()); // string(5) "value"

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

生成器 類實(shí)現(xiàn)了 生成器 接口卵蛉, 這意味著你必須遍歷 getValue() 方法來(lái)取值:

foreach (getValues() as $value) {
   echo $value;
}

// 使用變量也是好的
$values = getValues();
foreach ($values as $value) {
   echo $value;
}

但這不是唯一的不同!

一個(gè)生成器運(yùn)行你寫使用循環(huán)來(lái)迭代一維數(shù)組的代碼么库,而不需要在內(nèi)存中創(chuàng)建是一個(gè)數(shù)組傻丝,這可能會(huì)導(dǎo)致你超出內(nèi)存限制。

在下面的例子里我們創(chuàng)建一個(gè)有 800,000 元素的數(shù)字同時(shí)從 getValues() 方法中返回他诉儒,同時(shí)在此期間葡缰,我們將使用函數(shù) memory_get_usage() 來(lái)獲取分配給次腳本的內(nèi)存, 我們將會(huì)每增加 200,000 個(gè)元素來(lái)獲取一下內(nèi)存使用量,這意味著我們將會(huì)提出四個(gè)檢查點(diǎn):

<?php
function getValues() {
   $valuesArray = [];
   // 獲取初始內(nèi)存使用量
   echo round(memory_get_usage() / 1024 / 1024, 2) . ' MB' . PHP_EOL;
   for ($i = 1; $i < 800000; $i++) {
      $valuesArray[] = $i;
      // 為了讓我們能進(jìn)行分析泛释,所以我們測(cè)量一下內(nèi)存使用量
      if (($i % 200000) == 0) {
         // 來(lái) MB 為單位獲取內(nèi)存使用量
         echo round(memory_get_usage() / 1024 / 1024, 2) . ' MB'. PHP_EOL;
      }
   }
   return $valuesArray;
}
$myValues = getValues(); // 一旦我們調(diào)用函數(shù)將會(huì)在這里創(chuàng)建數(shù)組
foreach ($myValues as $value) {}

前面例子發(fā)生的情況是這個(gè)腳本的內(nèi)存消耗和輸出:

0.34 MB
8.35 MB
16.35 MB
32.35 MB

這意味著我們的幾行腳本消耗了超過(guò) 30 MB 的內(nèi)存滤愕, 每次你你添加一個(gè)元素到 $valuesArray 數(shù)組中怜校, 都會(huì)增加他在內(nèi)存中的大小茄茁。

讓我們使用 yield 同樣的例子:

<?php
function getValues() {
   // 獲取內(nèi)存使用數(shù)據(jù)
   echo round(memory_get_usage() / 1024 / 1024, 2) . ' MB' . PHP_EOL;
   for ($i = 1; $i < 800000; $i++) {
      yield $i;
      // 做性能分析,因此可測(cè)量?jī)?nèi)存使用率
      if (($i % 200000) == 0) {
         // 內(nèi)存使用以 MB 為單位
         echo round(memory_get_usage() / 1024 / 1024, 2) . ' MB'. PHP_EOL;
      }
   }
}
$myValues = getValues(); // 在循環(huán)之前都不會(huì)有動(dòng)作
foreach ($myValues as $value) {} // 開始生成數(shù)據(jù)

這個(gè)腳本的輸出令人驚訝:

0.34 MB
0.34 MB
0.34 MB
0.34 MB

這不意味著你從 return 表達(dá)式遷移到 yield裙顽,但如果你在應(yīng)用中創(chuàng)建會(huì)導(dǎo)致服務(wù)器上內(nèi)存出問(wèn)題的巨大數(shù)組付燥,則 yield 更加適合你的情況。


3. 什么是 "yield" 選項(xiàng)

這里有很多 yield 的選項(xiàng)锦庸, 我將強(qiáng)調(diào)他們中的幾個(gè):

a. 使用 yield机蔗, 你也可以使用 return

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

b. 返回鍵值對(duì):

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

點(diǎn)擊 這里 查看更多甘萧。

4. 結(jié)論

這個(gè)主題的主要原因是為了明確 yieldreturn 特別是在內(nèi)存使用方面的區(qū)別,使用一些例子是因?yàn)槲野l(fā)現(xiàn)他對(duì)任何開發(fā)人員思考真的很重要梆掸。

5. 參考

  1. http://php.net/manual/en/language.generators.syntax.php
  2. http://php.net/manual/en/class.generator.php
  3. http://php.net/manual/en/language.generators.php
  4. http://php.net/manual/en/function.memory-get-usage.php

討論請(qǐng)前往:https://laravel-china.org/topics/8704

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末扬卷,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子酸钦,更是在濱河造成了極大的恐慌怪得,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,907評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異欢伏,居然都是意外死亡入挣,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,987評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門硝拧,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)径筏,“玉大人,你說(shuō)我怎么就攤上這事障陶∽烫瘢” “怎么了?”我有些...
    開封第一講書人閱讀 164,298評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)勋拟。 經(jīng)常有香客問(wèn)我遏暴,道長(zhǎng),這世上最難降的妖魔是什么指黎? 我笑而不...
    開封第一講書人閱讀 58,586評(píng)論 1 293
  • 正文 為了忘掉前任朋凉,我火速辦了婚禮,結(jié)果婚禮上醋安,老公的妹妹穿的比我還像新娘杂彭。我一直安慰自己,他們只是感情好吓揪,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,633評(píng)論 6 392
  • 文/花漫 我一把揭開白布亲怠。 她就那樣靜靜地躺著,像睡著了一般柠辞。 火紅的嫁衣襯著肌膚如雪团秽。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,488評(píng)論 1 302
  • 那天叭首,我揣著相機(jī)與錄音习勤,去河邊找鬼。 笑死焙格,一個(gè)胖子當(dāng)著我的面吹牛图毕,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播眷唉,決...
    沈念sama閱讀 40,275評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼予颤,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了冬阳?” 一聲冷哼從身側(cè)響起蛤虐,我...
    開封第一講書人閱讀 39,176評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎肝陪,沒(méi)想到半個(gè)月后驳庭,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,619評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡见坑,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,819評(píng)論 3 336
  • 正文 我和宋清朗相戀三年嚷掠,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片荞驴。...
    茶點(diǎn)故事閱讀 39,932評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡不皆,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出熊楼,到底是詐尸還是另有隱情霹娄,我是刑警寧澤能犯,帶...
    沈念sama閱讀 35,655評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站犬耻,受9級(jí)特大地震影響踩晶,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜枕磁,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,265評(píng)論 3 329
  • 文/蒙蒙 一渡蜻、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧计济,春花似錦茸苇、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,871評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至传藏,卻和暖如春腻暮,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背毯侦。 一陣腳步聲響...
    開封第一講書人閱讀 32,994評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工哭靖, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人叫惊。 一個(gè)月前我還...
    沈念sama閱讀 48,095評(píng)論 3 370
  • 正文 我出身青樓款青,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親霍狰。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,884評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容