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