簡體   English   中英

使用解構和遞歸處理數組的函數的性能提高

[英]Increasing performance of a function which processes an array using destructuring and recursion

我想創建一個函數,該函數將另一個函數和一個數組作為參數,並為數組的每個三個連續元素(例如,第一,第二和第三;第二,第三和第四)調用該函數。 我使用解構和遞歸實現了它。 但是,我發現它的性能很差–處理大約1000個元素的數組大約需要100毫秒,並占用大量內存。 這是一個代碼片段:

 const eachThree = fn => ([first, second, third, ...rest]) => { fn(first, second, third); if (rest.length !== 0) { eachThree(fn)([second, third, ...rest]); } }; const noop = () => {}; const arr = Array(1000).fill(undefined); console.time('eachThree'); eachThree(noop)(arr); console.timeEnd('eachThree'); 

我知道要提高性能,我可以只使用常規的for循環,但是是否可以以某種方式修改此函數以使其更快,同時保持分解和遞歸?

另外,是否有計划優化JavaScript引擎以使這樣的代碼運行更快? 尾注優化會解決這個問題嗎?

處理1000個元素的數組大約需要100毫秒,並占用大量內存。

顯然,其原因是解構和散布語法,該語法創建了2000個數組,平均大小為500個元素。 那不便宜。

我知道要提高性能,我可以只使用常規的for循環,但是是否可以以某種方式修改此函數以使其更快,同時保持分解和遞歸?

放棄銷毀(支持索引,例如@Sylwester的答案)將是最好的解決方案,但實際上還有其他一些事情可以優化:

  • 使用return可以進行尾部調用優化(如果引擎支持的話)
  • 緩存內部函數,而不是一遍又一遍地使用相同的fn重新創建閉包。

const eachThree = fn => {
  const eachThreeFn = ([first, second, third, ...rest]) => {
    fn(first, second, third);
    if (rest.length !== 0) {
      return eachThreeFn([second, third, ...rest]);
    }
  };
  return eachThreeFn;
};

因此,我希望為您和我們着想,您確實需要縮短這200ms的時間,因為這對我而言似乎不是一個現實生活中的問題。 在優化規則中知道:

  1. 配置文件,以便您進行優化。

當涉及到優化時,整潔和風格無法實現。 這意味着您可以放棄遞歸。 但是,在這種情況下,不是最大的問題就是遞歸,而是要創建的2n數組。 這是一個稍快的版本:

const eachThree = fn => arr => {
  const maxLen = arr.length-3;
  const recur = (n) => {
    fn(arr[n], arr[n+1], arr[n+2]);
    if (n < maxLen) {
      recur(n+1);
    }
  }; 
  recur(0);
};

現在,ES6具有適當的尾部調用,而Node6已實現了此功能。 當我在那里測試時,它的運行速度比原始代碼快400倍。 並不是說您會注意到IRL的更改。

由於您似乎願意重構代碼,因此這是一個非常生動的重寫,它可以以大約10倍的速度(使用基准測試方法)執行相同的任務

我想您會欣賞到非常實用的樣式

 const drop = (n,xs) => xs.slice(n) const take = (n,xs) => xs.slice(0,n) const slide = (x,y) => xs => x > xs.length ? [] : [take (x,xs)] .concat (slide (x,y) (drop (y,xs))) const eachThree = f => xs => slide (3,1) (xs) .forEach (([a,b,c]) => f (a,b,c)) const noop = () => {} const arr = Array(1000).fill(undefined) console.time('eachThree') eachThree(noop)(arr) console.timeEnd('eachThree') 

使用蹦床可以解決內存使用問題,但是並不能加快代碼的速度。 這是一個代碼片段:

 const trampoline = fn => { do { fn = fn(); } while (typeof fn === 'function'); }; const eachThree = fn => ([first, second, third, ...rest]) => () => { fn(first, second, third); if (rest.length !== 0) { return eachThree(fn)([second, third, ...rest]); } }; const noop = () => {}; const arr = Array(5000).fill(undefined); console.time('eachThree'); trampoline(eachThree(noop)(arr)); console.timeEnd('eachThree'); 

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM