2017-09-04 1 views
0

Deuxième poste ici et celui-ci m'a vraiment gratté la tête. J'ai une fonction qui traite un tableau pour essayer de trouver des données similaires. Le tableau contient 1410 éléments que je considère être beaucoup mais rien que Node ou mon ordinateur ne devrait pas être capable de gérer.Node.js - Segfault: 11 Avec Quite Large Array

Mon code donne l'erreur "Segmentation Fault: 11" que j'ai trouvé concernait avec les problèmes d'accès à la mémoire, je veux même tester la mémoire de mon Mac mais tout va bien. Le segfault rend très difficile le débogage, c'est pourquoi je suis venu ici.

Le code où quelque chose va mal est à l'intérieur ici:

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 est le tableau dont je parle. common.getMode(array) a ce code:

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 

Il convient de mentionner que lorsqu'elle est effectuée avec un tableau de taille plus petite ~ 1000, le code fonctionne très bien qui me porte à croire qu'il est mon code. Il y a aussi très peu de contenu en ligne sur Node's Segfault 11 qui n'aide pas.

Toutes les idées grandement appréciées!

+0

Merci pour votre réponse. Le JSON du tableau qui ne fonctionne pas est ici: [link] (https://pastebin.com/SnVJM7xN) et celui qui fonctionne est ici: [link] (https://pastebin.com/GUYMMs6S). Est-il possible que cela soit dû à un délai d'attente en attente d'une promesse, car la fonction prend environ 8 secondes avant que le segfault ne se produise? –

Répondre

0

TL; DR supprime le stress du call stack en utilisant tail call optimization.

Modifier (avec explication)

Voir Understanding Javascript Functions Execution... pour comprendre la différence entre call stack, memory heap et queue. Alors que les objets et les variables vivent dans le pays heap, les appels de fonction sont référencés dans le call stack, que votre 2e jeu de données a appauvri (16'000 cadres de pile); Votre algorithme n'a donc pas pu suivre car il n'avait aucun moyen de continuer à allouer de nouveaux appels de fonction.

Voir this StackOverflow answer, qui pointe à plus d'informations sur la call stack et this one too, qui indique les moyens d'obtenir des données sur le heap.

réponse originale

je peux être complètement, mais je suis curieux de voir si la conversion de vos boucles pour récursion pourrait aider la mémoire à faire face. Je l'essayerais sur ma boîte, mais tout mettre en place est un problème.

Pourriez-vous essayer ça? Il utilise l'opérateur de propagation et la déstructuration des matrices, donc vous devrez peut-être ajouter babel-preset-stage-0 à votre projet et un fichier .babelrc aussi.

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); 

Modifier

Vous pouvez également appliquer l'optimisation des appels récursifs à votre boucle for en common.getMode(...).Le comportement de la première itération est différent, car lastName ne fait pas référence au nom de famille du tableau (index: -1), mais le premier. Voyez si cela correspond à vos besoins et optimisez un peu plus votre code.
Ceci devrait remplacer la boucle for dans common.getMode(...).

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

Vous semblez l'avoir résolu, merci beaucoup. Y at-il une limitation de Node qui a causé cela? Je surveillais l'utilisation de la mémoire lors de l'exécution de mon code et la mémoire me semblait correcte. Je pense que Node devrait être capable de gérer ça, n'est-ce pas? –

+0

C'est ce que l'on appelle l'optimisation de la queue. Fondamentalement, vous allez de la pile en croissance proportionnellement au nombre de ses arguments, à une pile qui devrait atteindre une taille fixe (en fonction du nombre de fois que vous branchez dans la fonction ou appelez d'autres fonctions). Je vais essayer de faire référence à de la littérature pour vous. Je ne pense pas que cela dépend de Node. C'est un problème qui surgira sur toutes les langues et l'ordinateur, à mon humble avis. Vous pouvez également optimiser votre boucle dans 'common.getMode (...)', mais le comportement change pour la première itération. Je vais l'écrire aussi comme un edit. –

+0

Voir [Optimisation des appels de queue dans ECMAScript 6] (http://2ality.com/2015/06/tail-call-optimization.html) et [Optimisation des appels de queue récursifs JS ES6] (https://medium.com/@ mlaythe/js-es6-recursif-tail-call-optimisation-feaf2dada3f6). L'idée est de ne pas se connecter à une autre fonction ou évaluation à la fin de votre fonction, et d'utiliser récursion pour faire la boucle pour vous. Vous pouvez également modifier la réponse si cela vous a été utile. –