2015-01-06 43 views
5

我有我要推很多值,以陣列的應用程序,所以我測試的執行時間:的setTimeout在JavaScript進行函數的運行速度

var st = new Date().getTime(); 
var a = []; 
for (var i = 0; i < 20971520; i++) { 
    a.push(i); 
} 
var ed = new Date().getTime(); 
console.info((ed - st)/1000); 
console.info(a.length); 

我直接運行在Firefox控制檯和Chrome控制檯代碼,它的價格是37 seconds。在執行過程中,即使鼠標可以在Chrome中移動,但沒有交互效果。

然後我改變了代碼:

function push() { 
    var st = new Date().getTime(); 
    var a = []; 
    for (var i = 0; i < 20971520; i++) { 
    a.push(i); 
    } 
    var ed = new Date().getTime(); 
    console.info((ed - st)/1000); 
    console.info(a.length); 
} 

var tr = setTimeout(push, 50); 

簡化放碼的功能,並使用setTimeout調用它,它的成本0.844 second。在執行過程中,我可以正常操作Chrome。

Screenshot of console

這是怎麼回事嗎?

我知道setTimeout會將控制權交給瀏覽器執行UI作業,這將使頁面響應。例如,當我在頁面的鼠標移動過程中進行一些計算時,我會延遲執行計算以防止它阻塞UI。

但是爲什麼它減少了相同代碼的總執行時間?

+1

也許這只是緩存。您是否嘗試交換執行順序,即先運行setTimeout變體,然後運行另一個變體? –

+0

如果你只是調用'push()'會發生什麼? – Freez

+0

無論有沒有超時,我都需要相同的時間。 – DoctorMick

回答

5

而在執行過程中,我可以正常運行在Chrome中。

不正確。主鉻窗口將像其他情況一樣凍結(只需要更短的時間)。調試工具是一個單獨的線程,但不會放慢速度。

但是爲什麼它減少了相同代碼的總執行時間?

它確實如果你在開發工具中運行。如果您在虛擬機可以進行屬性優化的實際中執行代碼,則時間可以相近(接近1秒)。例如

var st = new Date().getTime(); 
    var a = []; 
    for (var i = 0; i < 20971520; i++) { 
     a.push(i); 
    } 
    var ed = new Date().getTime(); 
    console.info('normal', (ed - st)/1000); 
    console.info(a.length); 

    function push() { 
     var st = new Date().getTime(); 
     var a = []; 
     for (var i = 0; i < 20971520; i++) { 
      a.push(i); 
     } 
     var ed = new Date().getTime(); 
     console.info('timeout', (ed - st)/1000); 
     console.info(a.length); 
    } 

    var tr = setTimeout(push, 0); 

http://jsfiddle.net/gu9Lg52j/你會看到normal執行一樣快setTimeout

另外,如果你在一個功能包的代碼,並在控制檯執行時間會比較的,即使沒有一個setTimeout因爲虛擬機可以進行函數定義和執行之間的優化:

function push() { 
     var st = new Date().getTime(); 
     var a = []; 
     for (var i = 0; i < 20971520; i++) { 
      a.push(i); 
     } 
     var ed = new Date().getTime(); 
     console.info('timeout', (ed - st)/1000); 
     console.info(a.length); 
    } 
    push(); 
+0

你的小提琴使用'onload'來改變代碼的執行方式。 –

+0

@SalmanA你是對的。我改變了小提琴執行原始和結果仍然可比。 – basarat

1

所有的JavaScript引擎執行各種優化。例如V8使用2個編譯器,默認情況下使用一個簡單的編譯器,並優化一個。由優化編譯器編譯的代碼是,非常慢。

優化編譯器運行的條件是代碼必須位於(不是太長)函數中(there are other conditions)。您在控制檯中嘗試的第一個代碼不在函數中。把你的第一個代碼放在一個函數中,你會看到它的執行效果與第二個相同,setTimeout不會改變任何內容。

當主要性能因素是優化編譯時,檢查控制檯中的性能毫無意義。如果您要定位節點,請使用基準測試框架。如果您針對的是瀏覽器,請使用類似jsperf的網站。

現在,當你必須在瀏覽器中做一個非常長的計算(這裏似乎不是這種情況),你應該考慮using web workers哪些在後臺線程中執行這項工作不會影響UI。

+0

如果我必須在Nodewebkit中運行長計算,那麼我想知道創建一個包含數百萬個元素的數組是否會阻止UI?這就是我測試這個功能的原因。 – hguser

2

代碼的兩種變體應該以幾乎相同的速度運行(後者的示例可能會更快但速度不會快10倍)。

在Chrome開發人員工具中,有一個不同的故事。表達式在with塊內進行評估。這意味着首先在另一個對象(__commandLineAPI)內搜索變量(如ai)。這增加了額外的開銷,導致執行時間延長了10倍。

+0

這不是真正的原因。功能(你的IIFE)是原因。 –

+0

問題是,在這個例子中,你正在推入一個局部變量'a'而不是'window.a'。 –

+0

是的,但這並不影響性能,而是將代碼放入函數中(嘗試使用'window.a') –

0

setTimeout,像其他人一樣注意到,並沒有加快陣列的創建速度,並且確實鎖定了瀏覽器。如果您在創建陣列期間擔心瀏覽器鎖定,Web工作人員(請參閱MDN)可能會救援。這裏是一個jsFiddle演示使用Web工作人員爲您的代碼。工作代碼在html中:

onmessage = function (e) { 
    var a = [], now = new Date; 
    for (var i=0; i<20971520; i++) { 
    a.push(i); 
    } 
    postMessage({timings: 'duration: '+(new Date()-now) + 
         'Ms, result: [' + a[0] + '...'+a[a.length-1] + ']'}); 
}