2017-09-04 71 views
0

這裏的第二篇文章,這真的讓我撓了撓頭。我有一個處理數組的函數來試圖找到類似的數據。該數組包含1410個元素,我認爲它很多,但Node或我的計算機不應該能夠處理的東西。Node.js - Segfault:11與相當大的陣列

我的代碼給出了「Segmentation Fault:11」錯誤,我發現它是與內存訪問問題有關的,所以我甚至希望儘可能測試我的Mac的RAM,但一切正常。 segfault使調試非常困難,這就是我來到這裏的原因。

那裏事情錯的代碼中的位置:

return matchings.map(matchArray => { 
    const namesList = matchArray 
    .map(matchItem => matchItem.name) 
    .sort((a, b) => a.localeCompare(b)) 

    const symbolsList = matchArray 
    .map(matchItem => matchItem.symbol) 
    .sort((a, b) => a.localeCompare(b)) 

    return { 
    name: common.getMode(namesList), 
    symbol: common.getMode(symbolsList), 
    matches: matchArray 
    } 
}).sort((a, b) => a.name.localeCompare(b.name)) 

哪裏matchings是我說的數組。 common.getMode(array)有這樣的代碼:

array.sort() 

const stats = { 
    top: { 
    name: '', 
    freq: 0 
    }, 
    current: { 
    name: array[0], 
    freq: 1 
    } 
} 

for (let idxName = 1; idxName < array.length; idxName++) { 
    const currentName = array[idxName] 
    const lastName = array[idxName - 1] 

    if (currentName === lastName) { 
    stats.current.freq++ 
    } else { 
    if (stats.current.freq > stats.top.freq) { 
     stats.top.name = stats.current.name 
     stats.top.freq = stats.current.freq 
    } 
    stats.current = { 
     name: currentName, 
     freq: 1 
    } 
    } 
} 

if (stats.current.freq > stats.top.freq) { 
    stats.top.name = stats.current.name 
    stats.top.freq = stats.current.freq 
} 

return stats.top.name 

值得一提的是,與小尺寸〜1000的數組進行時,代碼工作正常,這使我相信這不是我的代碼。網上關於Node的Segfault 11的內容也很少,這沒有幫助。

任何想法非常感謝!

+0

感謝您的回覆。非工作陣列的JSON在這裏:[鏈接](https://pastebin.com/SnVJM7xN),工作在這裏:[link](https://pastebin.com/GUYMMs6S)。有沒有什麼辦法可能是因爲等待承諾超時,因爲該函數需要大約8秒纔會發生段錯誤? –

回答

0

TL; DR使用tail call optimization消除來自call stack的壓力。

編輯(有解釋)

Understanding Javascript Functions Execution...瞭解call stackmemory heapqueue之間的差異。儘管對象和變量存在於heap陸地中,但函數調用在call stack中引用,第二個數據集耗盡(16'000個堆棧幀);所以你的算法無法跟上,因爲它無法繼續分配新的函數調用。

this StackOverflow answer,它指向有關call stackthis one too進一步的信息,它指向的方式來獲得對heap數據。

原來的答覆

我可以完全離開,但我很好奇,看看是否轉換您循環,以遞歸可以幫助內存應付。我會在我的盒子上嘗試它,但設置一切都很麻煩。

你可以試試嗎?它使用擴展運算符和數組解構,因此您可能需要將babel-preset-stage-0添加到您的項目以及.babelrc文件中。

的Javascript

let common = {}; 
common.getMode = (arr, compare_fn) => { 
    const compare = !!compare_fn ? compare_fn : (a, b) => a.localeCompare(b) 
    arr.sort(compare) 

    const stats = { 
    top: { 
     name: '', 
     freq: 0 
    }, 
    current: { 
     name: arr[0], 
     freq: 1 
    } 
    } 

    for (let i=1, imax = arr.length ; i < imax ; ++i) { 
    const currentName = arr[i] 
    const lastName = arr[i - 1] 

    if (currentName === lastName) { 
     stats.current.freq++ 
    } else { 
     if (stats.current.freq > stats.top.freq) { 
     stats.top.name = stats.current.name 
     stats.top.freq = stats.current.freq 
     } 
     stats.current = { 
     name: currentName, 
     freq: 1 
     } 
    } 
    } 

    if (stats.current.freq > stats.top.freq) { 
    stats.top.name = stats.current.name 
    stats.top.freq = stats.current.freq 
    } 

    return stats.top.name 
}; 

const build_prop_list = (prop, input_array, output_array = []) => { 
    if(input_array.length == 0) return output_array; 
    else { 
    const [current, ...tail] = input_array; 
    const new_array = [...output_array, current[prop]]; 
    return build_prop_list(prop, tail, new_array); 
    } 
} 

const work = (input_array, output_array = []) => { 
    if(input_array.length == 0) return output_array.sort((a, b) => a.name.localeCompare(b.name)); 
    else { 
    const [matchArray, ...tail] = input_array; 

    const namesList = build_prop_list("name", matchArray); 
    const symbolsList = build_prop_list("symbol", matchArray); 

    const new_element = { 
     name: common.getMode(namesList), 
     symbol: common.getMode(symbolsList), 
     matches: matchArray 
    }; 

    const new_array = [...output_array, new_element]; 
    return work(tail, new_array); 
    } 
} 

let result = work(insert_your_json_here); 

編輯

您也可以申請尾部調用優化for環路common.getMode(...)。雖然第一次迭代的行爲不同,因爲lastName未引用數組的姓(索引:-1),但是第一個。看看它是否符合你的需求,並且你會優化你的代碼。
這應取代common.getMode(...)中的for循環。

const feed = (input_array) => { 
    if(input_array.length == 0) return; 
    const [lastName, currentName, ...tail] = input_array; 

    if (currentName === lastName) { 
     stats.current.freq++ 
    } else { 
     if (stats.current.freq > stats.top.freq) { 
     stats.top.name = stats.current.name 
     stats.top.freq = stats.current.freq 
     } 
     stats.current = { 
     name: currentName, 
     freq: 1 
     } 
    } 

    return feed(tail); 
    }(arr); 
+0

你似乎已經解決了,非常感謝。是否有節點造成這種情況的限制?我正在監視內存使用情況,同時運行我的代碼,內存似乎很好。我認爲Node應該能夠處理這個問題,不是嗎? –

+0

這叫做尾呼優化。基本上,你從堆棧增長到與它的參數數量成比例增長,到堆棧應該增長到一個固定的大小(取決於你在這個函數中分支多少次或者調用其他函數)。我會盡力爲你提供一些文獻。我不認爲它依賴於Node。這是一個會在任何語言和計算機上出現的問題,恕我直言。你也可以在'common.getMode(...)'中優化你的循環,但是第一次迭代的行爲會改變。我也會編輯它。 –

+0

請參閱[ECMAScript 6中的尾部呼叫優化](http://2ality.com/2015/06/tail-call-optimization.html)和[JS ES6遞歸尾部呼叫優化](https://medium.com/@ mlaythe/JS-ES6遞歸尾叩優化-feaf2dada3f6)。這個想法是不要在你的函數結尾分支到另一個函數或者評估中,並且使用遞歸爲你做循環。如果它對你有幫助,你也可以提出答案。 –