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