摘要:原文作者譯文如何百倍加速引入惰性計(jì)算譯者我一直以為像這樣的庫(kù)已經(jīng)不能再快了,畢竟它們已經(jīng)足夠快了。函數(shù)返回價(jià)格低于的所有元素。延遲執(zhí)行和惰性計(jì)算一起使用的是延遲執(zhí)行。懶惰計(jì)算并不是行業(yè)里的新理念。
原文:How to Speed Up Lo-Dash ×100? Introducing Lazy Evaluation.
作者: Filip Zawada
譯文:如何百倍加速 Lo-Dash?引入惰性計(jì)算
譯者:justjavac
我一直以為像 Lo-Dash 這樣的庫(kù)已經(jīng)不能再快了,畢竟它們已經(jīng)足夠快了。
Lo-Dash 幾乎完全混合了各種 JavaScript 奇技淫巧(YouTube)來(lái)壓榨出最好的性能。
但似乎我錯(cuò)了 - 其實(shí) Lo-Dash 可以運(yùn)行的更快。
你需要做的是,停止思考那些細(xì)微的優(yōu)化,并開(kāi)始找出更加適用的算法。
例如,在一個(gè)典型的循環(huán)中,我們往往傾向于去優(yōu)化單次迭代的時(shí)間:
var len = getLength(); for(var i = 0; i < len; i++) { operation(); // <- 10毫秒 - 如何優(yōu)化到9毫秒?! }
代碼說(shuō)明:取得數(shù)組的長(zhǎng)度,然后重復(fù)執(zhí)行 N 遍 operation() 函數(shù)。譯注 by @justjavac
但是,這(優(yōu)化 operation() 執(zhí)行時(shí)間)往往很難,而且對(duì)性能提升也非常有限。
相反,在某些情況下,我們可以?xún)?yōu)化 getLength() 函數(shù)。
它返回的數(shù)字越小,則每個(gè) 10 毫秒循環(huán)的執(zhí)行次數(shù)就越少。
這就是 Lo-Dash 使用惰性計(jì)算的思想。
這是減少周期數(shù),而不是減少每個(gè)周期的執(zhí)行時(shí)間。
讓我們看看下面的例子:
function priceLt(x) { return function(item) { return item.price < x; }; } var gems = [ { name: "Sunstone", price: 4 }, { name: "Amethyst", price: 15 }, { name: "Prehnite", price: 20 }, { name: "Sugilite", price: 7 }, { name: "Diopside", price: 3 }, { name: "Feldspar", price: 13 }, { name: "Dioptase", price: 2 }, { name: "Sapphire", price: 20 } ]; var chosen = _(gems).filter(priceLt(10)).take(3).value();
代碼說(shuō)明:gems 保存了 8 個(gè)對(duì)象,名字和價(jià)格。priceLt(x) 函數(shù)返回價(jià)格低于 x 的所有元素。譯注 by @justjavac
我們把價(jià)格低于 10 美元的前 3 個(gè) gems 找出來(lái)。
常規(guī) Lo-Dash 方法(嚴(yán)格計(jì)算)是過(guò)濾所有 8 個(gè) gems,然后返回過(guò)濾結(jié)果的前 3 個(gè)。
不難看出來(lái),這種算法是不明智的。
它處理了所有的 8 個(gè)元素,而實(shí)際上我們只需要讀取其中的 5 個(gè)元素就能得到我們想要的結(jié)果。
與此相反,使用惰性計(jì)算算法,只需要處理能得到結(jié)果的最少數(shù)量就可以了。
如圖所示:
我們輕而易舉就獲得了 37.5% 的性能提升。
但是這還不是全部,其實(shí)很容易找到能獲得 1000 倍以上性能提升的例子。
讓我們一起來(lái)看看:
// 99,999 張照片 var phoneNumbers = [5554445555, 1424445656, 5554443333, … ×99,999]; // 返回包含 "55" 的照片 function contains55(str) { return str.contains("55"); }; // 取 100 張包含 "55" 的照片 var r = _(phoneNumbers).map(String).filter(contains55).take(100);
在這個(gè)例子中,map 和 filter 用來(lái)處理 99,999 個(gè)元素。
不過(guò)我們只需要它的一個(gè)子集就可以得到想要的結(jié)果了,例如 10,000 個(gè),
性能提升也是非常大的(基準(zhǔn)測(cè)試):
惰性計(jì)算帶來(lái)了另一個(gè)好處,我稱(chēng)之為 "Pipelining"。
它可以避免鏈?zhǔn)椒椒▓?zhí)行期間創(chuàng)建中間數(shù)組。
取而代之,我們?cè)趩蝹€(gè)元素上執(zhí)行所有操作。
所以,下面的代碼:
var result = _(source).map(func1).map(func2).map(func3).value();
將大致翻譯為如下的常規(guī) Lo-Dash(嚴(yán)格計(jì)算)
var result = [], temp1 = [], temp2 = [], temp3 = []; for(var i = 0; i < source.length; i++) { temp1[i] = func1(source[i]); } for(i = 0; i < source.length; i++) { temp2[i] = func2(temp1[i]); } for(i = 0; i < source.length; i++) { temp3[i] = func3(temp2[i]); } result = temp3;
如果我們使用惰性計(jì)算,它會(huì)像下面這樣執(zhí)行:
var result = []; for(var i = 0; i < source.length; i++) { result[i] = func3(func2(func1(source[i]))); }
不使用臨時(shí)數(shù)組可以給我們帶來(lái)非常顯著的性能提升,特別是當(dāng)源數(shù)組非常大時(shí),內(nèi)存訪(fǎng)問(wèn)是昂貴的資源。
延遲執(zhí)行和惰性計(jì)算一起使用的是延遲執(zhí)行。
當(dāng)你創(chuàng)建一個(gè)鏈,我們并不立即計(jì)算它的值,直到 .value() 被顯式或者隱式地調(diào)用。
這種方法有助于先準(zhǔn)備一個(gè)查詢(xún),隨后我們使用最新的數(shù)據(jù)來(lái)執(zhí)行它。
var wallet = _(assets).filter(ownedBy("me")) .pluck("value") .reduce(sum); $json.get("/new/assets").success(function(data) { assets.push.apply(assets, data); // 更新我的資金 wallet.value(); // 返回我錢(qián)包的最新的總額 });
在某些情況下,這樣做也可以加速執(zhí)行時(shí)間。我們可以在前期創(chuàng)建復(fù)雜的查詢(xún),然后當(dāng)時(shí)機(jī)成熟時(shí)再執(zhí)行它。
Wrap up懶惰計(jì)算并不是行業(yè)里的新理念。它已經(jīng)包含在了許多庫(kù)里面,例如 LINQ、Lazy.js 等等。我相信 Lo-Dash 和這些庫(kù)最主要的區(qū)別是,你可以在一個(gè)更新的、更強(qiáng)大的引擎里面使用原有的 Underscore API。不需要學(xué)習(xí)新的庫(kù),不需要修改代碼,只是簡(jiǎn)單升級(jí)。
但是,即使你不打算使用 Lo-Dash,我希望這篇文章啟發(fā)了你。
現(xiàn)在,當(dāng)你發(fā)現(xiàn)你的應(yīng)用程序存在性能瓶頸,不要僅僅是去 jsperf.com 以 try/fail 風(fēng)格優(yōu)化它。
而是去喝杯咖啡,并開(kāi)始考慮算法。
最重要的是創(chuàng)意,但良好的數(shù)學(xué)背景會(huì)讓你如魚(yú)得水(book)。祝你好運(yùn)!
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/78316.html
摘要:本文將講述源碼中,惰性求值的原理和實(shí)現(xiàn)。惰性求值中的參數(shù)直到需要時(shí)才會(huì)進(jìn)行計(jì)算。執(zhí)行的示例圖如下惰性求值做法普通的做法存在一個(gè)問(wèn)題每個(gè)方法各做各的事,沒(méi)有協(xié)調(diào)起來(lái)浪費(fèi)了很多資源。 前言 lodash受歡迎的一個(gè)原因,是其優(yōu)異的計(jì)算性能。而其性能能有這么突出的表現(xiàn),很大部分就來(lái)源于其使用的算法——惰性求值。本文將講述lodash源碼中,惰性求值的原理和實(shí)現(xiàn)。 一、惰性求值的原理分析 惰性...
摘要:我用替換已經(jīng)有一段時(shí)間了。更快,支持,并且擁有所缺乏的特性。這真是太棒了同樣聲稱(chēng)類(lèi)似,但是使用惰性求值,并發(fā)布了一些令人印象深刻的速度比較。如果你使用,不管在哪里使用包括,你應(yīng)該花上幾分鐘切換到。 我用Lo-Dash替換Underscore已經(jīng)有一段時(shí)間了。Lo-Dash更快,支持AMD,并且擁有Underscore所缺乏的特性。同時(shí),Lo-Dash和Underscore是100%兼容...
摘要:原文可觀(guān)察量是一種能惰性推送的集合,他可以包含多個(gè)值。是一種惰性計(jì)算方式,會(huì)在迭代中同步的返回到無(wú)限個(gè)可能的話(huà)返回值。使用一種處理方法,最終可能會(huì)或可能不會(huì)返回一個(gè)值。無(wú)論是同步方式還是異步方式,都可以擇其一來(lái)傳遞返回值。 原文:http://reactivex.io/rxjs/manu... Observable 可觀(guān)察量是一種能惰性推送的集合,他可以包含多個(gè)值。下面的表格對(duì)比了推送...
摘要:然而學(xué)習(xí)布局,你只要學(xué)習(xí)幾個(gè)手機(jī)端頁(yè)面自適應(yīng)解決方案布局進(jìn)階版附源碼示例前端掘金一年前筆者寫(xiě)了一篇手機(jī)端頁(yè)面自適應(yīng)解決方案布局,意外受到很多朋友的關(guān)注和喜歡。 十分鐘學(xué)會(huì) Fiddler - 后端 - 掘金一.Fiddler介紹 Fiddler是一個(gè)http抓包改包工具,fiddle英文中有欺騙、偽造之意,與wireshark相比它更輕量級(jí),上手簡(jiǎn)單,因?yàn)橹荒茏ttp和https數(shù)據(jù)...
摘要:譯文地址譯唯快不破應(yīng)用的個(gè)優(yōu)化步驟前端的逆襲知乎專(zhuān)欄原文地址時(shí)過(guò)境遷,應(yīng)用比以往任何時(shí)候都更具交互性。使用負(fù)載均衡方案我們?cè)谥坝懻摼彺娴臅r(shí)候簡(jiǎn)要提到了內(nèi)容分發(fā)網(wǎng)絡(luò)。換句話(huà)說(shuō),元素的串形訪(fǎng)問(wèn)會(huì)削弱負(fù)載均衡器以最佳形式 歡迎關(guān)注知乎專(zhuān)欄 —— 前端的逆襲歡迎關(guān)注我的博客,知乎,GitHub。 譯文地址:【譯】唯快不破:Web 應(yīng)用的 13 個(gè)優(yōu)化步驟 - 前端的逆襲 - 知乎專(zhuān)欄原文地...
閱讀 1643·2023-04-25 18:27
閱讀 1396·2021-10-19 11:44
閱讀 572·2021-10-14 09:42
閱讀 2147·2021-10-11 10:59
閱讀 2779·2021-09-24 09:47
閱讀 1729·2019-08-30 14:20
閱讀 1161·2019-08-30 14:08
閱讀 740·2019-08-29 15:15