摘要:幾乎完美結(jié)合了各種各樣的技術(shù)來榨干了的性能。它使用了最快的語句,自適應(yīng)的算法,它甚至進行性能測試以避免在后續(xù)發(fā)布的版本中意外的降低了性能。這就是中背后的基本思想減少循環(huán)的次數(shù)而不是減少每次循環(huán)的時間。
原文:http://filimanjaro.com/blog/2...
我曾經(jīng)一直認為像Lodash這樣的庫是不可能正的比它們已有的速度更快的。Lodash幾乎完美結(jié)合了各種各樣的技術(shù)來榨干了JavaScript的性能。它使用了JavaScript最快的語句,自適應(yīng)的算法,它甚至進行性能測試以避免在后續(xù)發(fā)布的版本中意外的降低了性能。
惰性計算Lazy Evaluation但是貌似我錯了——實際上我們能讓Lodas運行速度得到明顯提升。而你所需要做的僅僅只是停止考慮一些微觀的優(yōu)化,然后找到并使用更好的算法。例如,在一個典型的循環(huán)中,我們通常傾向于優(yōu)化每次迭代的時間:
var len = getLength(); for(var i = 0; i < len; i++) { operation(); // <- 10ms - how to make it 9ms?! }
但是這樣的優(yōu)化很有限而且很難做到優(yōu)化。相反的,在某些情況下優(yōu)化getLength()函數(shù)卻有意義的多。getLength()函數(shù)返回的數(shù)字越小,我們要執(zhí)行的10ms的循環(huán)次數(shù)就越少。
這就是Lodash中l(wèi)azy evaluation背后的基本思想:減少循環(huán)的次數(shù)而不是減少每次循環(huán)的時間。讓我們看一看下面的例子:
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();
我們只想提取3個價格低于$10的寶石(gem)。常規(guī)Lodash方法(Strict evaluation)先篩選所有的8個寶石,然后返回前三個通過篩選的寶石。如下圖:
然而,這還不夠酷。它處理了所有的8個元素,然而實際上我們只需要讀取其中的5個元素。相反的,Lazy evaluation算法只處理數(shù)組中最少的元素數(shù)量即可獲得正確的結(jié)果,看如下實例:
這樣我們就輕松的獲取了37.5%的性能提升,但37.5%不是我們所能達到的極限,實際上很容易就能找到一個帶來100倍性能提升的例子。讓我們看下面的例子:
var phoneNumbers = [5554445555, 1424445656, 5554443333, … ×99,999]; // get 100 phone numbers containing ?55” function contains55(str) { return str.contains("55"); }; var r = _(phoneNumbers).map(String).filter(contains55).take(100);
在這個例子中,map和filter要在99999個元素上執(zhí)行,而實際上,它有可能只需要在少數(shù)元素,例如1000個元素上運行就足夠了,在這個例子中,Lazy evaluation帶來的性能提升非常巨大(benchmark)
PipeliningLazy evaluation帶來的另外一個好處被稱為"pipelineing",這個概念背后的思想是避免在鏈式執(zhí)行(chain execution)中創(chuàng)建中間數(shù)組。我們應(yīng)該在一個元素上原地執(zhí)行所有的操作。所以下面的代碼
var result = _(source).map(func1).map(func2).map(func3).value();
在regulaer Lodash常規(guī)計算中(strict evaluation)將大致的轉(zhuǎn)換為這樣:
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;
而如果在惰性計算(lazy evaluation)打開以后,它將像這樣執(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í)行(Deferred execution)和lazy evaluation一起帶來的另外一個好處就是延遲執(zhí)行(deferred execution),不管什么時候你創(chuàng)建了一個chain,在顯式或者隱式調(diào)用.value()方法前它都不會計算。這個特性使我們能先準備一個查詢,然后再在在最新的數(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); // update assets wallet.value(); // returns most up-to-date value });
同時因為Lazy evaluation可也以加速執(zhí)行時間,所以在運行時間很重要的時候,我們可以提前創(chuàng)建一個復(fù)雜的查詢,然后再執(zhí)行它。
Wrap upLazy evaluation在行業(yè)中并不是一個新想法,它已經(jīng)在例如LINQ,Lazy.js和很多其它的優(yōu)秀的庫中存在了。我認為Lodash不同于他們的地方主要在于你仍然擁有一個外表和Underscore一樣的API,但內(nèi)部卻是一個新的強大的引擎。不需要學(xué)習(xí)新的庫,不需要更改大量的代碼,就像只是一個附加的更新一樣。
但是,即使你沒有打算使用Lodash,我也希望這篇文章能給你帶來一點啟發(fā):當(dāng)你發(fā)現(xiàn)你的應(yīng)用中的瓶頸時,不要在jsperf.com中try/fail的來優(yōu)化它,而是起身,沖一杯咖啡,然后開始考慮一下算法。在這里創(chuàng)新的確很重要,但是良好的數(shù)學(xué)背景不會有害(推薦書目),祝你好運!
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/87940.html
摘要:本文將講述源碼中,惰性求值的原理和實現(xiàn)。惰性求值中的參數(shù)直到需要時才會進行計算。執(zhí)行的示例圖如下惰性求值做法普通的做法存在一個問題每個方法各做各的事,沒有協(xié)調(diào)起來浪費了很多資源。 前言 lodash受歡迎的一個原因,是其優(yōu)異的計算性能。而其性能能有這么突出的表現(xiàn),很大部分就來源于其使用的算法——惰性求值。本文將講述lodash源碼中,惰性求值的原理和實現(xiàn)。 一、惰性求值的原理分析 惰性...
摘要:同時還定義了接口,使得其下級可以從這里得到一個迭代器,對于該進行遍歷。迭代器在中也是一個約定的協(xié)議,實現(xiàn)該協(xié)議的對象要支持和兩個接口方法。從迭代器的邏輯中,可以看到,當(dāng)對象作為其他的上級時,如果實現(xiàn)上傳下達。 背景:惰性求值? 來看一個 lazy.js 主頁提供的示例: var people = getBigArrayOfPeople(); var results = _.chain(...
摘要:構(gòu)建是為了在中為常見任務(wù)提供實用程序功能。所有功能都自動進行,并且相應(yīng)地安排傳遞的參數(shù)以便于使用。在星級,是一個用于處理本機對象的實用程序庫。該庫沒有外部依賴關(guān)系,這是一個將事件作為序列進行測試的現(xiàn)場演示。 由于Javascript在2018年仍然是最受歡迎和最廣泛使用的編程語言,因此圍繞它擴展了生態(tài)系統(tǒng)。 showImg(https://segmentfault.com/img/re...
摘要:構(gòu)建是為了在中為常見任務(wù)提供實用程序功能。所有功能都自動進行,并且相應(yīng)地安排傳遞的參數(shù)以便于使用。在星級,是一個用于處理本機對象的實用程序庫。該庫沒有外部依賴關(guān)系,這是一個將事件作為序列進行測試的現(xiàn)場演示。 由于Javascript在2018年仍然是最受歡迎和最廣泛使用的編程語言,因此圍繞它擴展了生態(tài)系統(tǒng)。 showImg(https://segmentfault.com/img/re...
摘要:而純函數(shù),主要強調(diào)相同的輸入,多次調(diào)用,輸出也相同且無副作用。對于組合可能不返回值的函數(shù)很有用在其它的一些地方,也稱為,也稱為,也稱為 參考文檔1 參考文檔2 函數(shù)式編程術(shù)語 高階函數(shù) Higher-Order Functions 以函數(shù)為參數(shù)的函數(shù) 返回一個函數(shù)的函數(shù) 函數(shù)的元 Arity 比如,一個帶有兩個參數(shù)的函數(shù)被稱為二元函數(shù) 惰性求值 Lazy evaluation 是...
閱讀 2938·2021-10-14 09:43
閱讀 2883·2021-10-14 09:42
閱讀 4663·2021-09-22 15:56
閱讀 2371·2019-08-30 10:49
閱讀 1594·2019-08-26 13:34
閱讀 2385·2019-08-26 10:35
閱讀 605·2019-08-23 17:57
閱讀 2029·2019-08-23 17:15