Pregunta
Y a-t-il quelque chose qui ne va pas dans mon benchmark ? Comment Immutable.js find() peut-il être 8 fois plus lent que array.find() ?
Ok, ce n'est pas tout à fait juste, puisque j'utilise Immutable.Map à l'intérieur de Immutable.List. Mais pour moi, c'est un exemple concret. Si j'utilise Immutable.js, c'est pour protéger l'immuabilité et pour gagner en performance sur certains aspects (où le partage structurel entre en jeu). Il n'y aurait aucun intérêt à utiliser Immutable.js uniquement à la racine de l'objet.
Le repère ci-dessous provient en fait de une autre question (le mien aussi). J'ai été tellement surpris par les résultats que j'ai dû le poster séparément pour bien comprendre. Ai-je fait quelque chose de mal dans mes benchmarks, ou la différence de performance est-elle vraiment si importante ?
Contexte
Certaines des données de mon application pourraient être considérées comme des métadonnées d'application. Les données originales se trouvent dans une base de données sur le serveur. Les mises à jour des métadonnées ne sont pas fréquentes. L'application vérifiera la mise à jour des métadonnées au démarrage.
J'utilise Immutable.js partout, mais je vais revenir au js simple pour les métadonnées. Il n'y a pas besoin de partage structurel fantaisiste pour ce type de données.
Le test consiste à trouver les valeurs par clé dans une collection
-
Collection de 10 articles
-
Trouver une valeur un million de fois
-
Mac mini core i7 2.6
Résultat :
-
Objet JS simple avec des clés coercitives : 8 ms
-
Tableau JS simple utilisant find() : 127 ms
-
Immuable.Map avec des clés numériques : 185 ms
-
Immutable.List en utilisant find() : 972 ms ! ! Je suis déconcerté
Comme j'utilise React Native, je dois toujours faire attention à la limite de 16 ms si je veux atteindre 60 fps. Les valeurs de référence ne semblent pas être linéaires. L'exécution du test avec seulement 100 consultations prend 1 ms avec Map et 2 ms avec List. C'est assez coûteux.
Code de test
let Immutable = require('immutable');
let mapTest = Immutable.Map()
.set(1, Immutable.Map({value: 'one'}))
.set(2, Immutable.Map({value: 'two'}))
.set(3, Immutable.Map({value: 'three'}))
.set(4, Immutable.Map({value: 'four'}))
.set(5, Immutable.Map({value: 'five'}))
.set(6, Immutable.Map({value: 'six'}))
.set(7, Immutable.Map({value: 'seven'}))
.set(8, Immutable.Map({value: 'eight'}))
.set(9, Immutable.Map({value: 'nine'}))
.set(10, Immutable.Map({value: 'ten'}));
let listTest = Immutable.fromJS([
{key: 1, value: 'one'},
{key: 2, value: 'two'},
{key: 3, value: 'three'},
{key: 4, value: 'four'},
{key: 5, value: 'five'},
{key: 6, value: 'six'},
{key: 7, value: 'seven'},
{key: 8, value: 'eight'},
{key: 9, value: 'nine'},
{key: 10, value: 'ten'}
])
let objTest = {
1: {value: 'one'},
2: {value: 'two'},
3: {value: 'three'},
4: {value: 'four'},
5: {value: 'five'},
6: {value: 'six'},
7: {value: 'seven'},
8: {value: 'eight'},
9: {value: 'nine'},
10: {value: 'ten'}
};
let arrayTest = [
{key: 1, value: 'one'},
{key: 2, value: 'two'},
{key: 3, value: 'three'},
{key: 4, value: 'four'},
{key: 5, value: 'five'},
{key: 6, value: 'six'},
{key: 7, value: 'seven'},
{key: 8, value: 'eight'},
{key: 9, value: 'nine'},
{key: 10, value: 'ten'}
];
const runs = 1e6;
let i;
let key;
let hrStart;
console.log(' ')
console.log('mapTest -----------------------------')
key = 1;
hrstart = process.hrtime();
for(i=0; i<runs; i++) {
let result = mapTest.getIn([key, 'value'] )
key = (key >= 10) ? 1 : key + 1;
}
hrend = process.hrtime(hrstart);
console.info("Execution time (hr): %dms", hrend[0] * 1000 + hrend[1]/1000000);
console.log(' ')
console.log('listTest -----------------------------')
key = 1;
hrstart = process.hrtime();
for(i=0; i<runs; i++) {
let result = listTest
.find(item => item.get('key') === key)
.get('value');
key = (key >= 10) ? 1 : key + 1;
}
hrend = process.hrtime(hrstart);
console.info("Execution time (hr): %dms", hrend[0] * 1000 + hrend[1]/1000000);
console.log(' ')
console.log('arrayTest -----------------------------')
key = 1;
hrstart = process.hrtime();
for(i=0; i<runs; i++) {
let result = arrayTest
.find(item => item.key === key)
.value
key = (key >= 10) ? 1 : key + 1;
}
hrend = process.hrtime(hrstart);
console.info("Execution time (hr): %dms", hrend[0] * 1000 + hrend[1]/1000000);
console.log(' ')
console.log('objTest -----------------------------')
key = 1;
hrstart = process.hrtime();
for(i=0; i<runs; i++) {
let result = objTest[key].value
key = (key >= 10) ? 1 : key + 1;
}
hrend = process.hrtime(hrstart);
console.info("Execution time (hr): %dms", hrend[0] * 1000 + hrend[1]/1000000);
1 votes
Quelle est la question ? Recherches utilisant
.find()
seront bien sûr plus lentes que les recherches par clé.0 votes
Désolé. J'ai maintenant mis l'accent sur la question.
0 votes
"Ai-je fait quelque chose de mal dans mes benchmarks, ou la différence de performance est-elle vraiment si importante ?" Qu'est-ce que
process
?0 votes
Vous alambiquez la question en ajoutant une carte, cela n'a aucun sens de comparer une recherche sur une carte avec une recherche sur un tableau.
0 votes
Pour moi, c'est logique. Si je dois utiliser Immutable.js, cela ne sert à rien de l'utiliser à moitié. C'est pourquoi mes exemples sont construits autour d'alternatives d'utilisation dans le monde réel (pour moi en tout cas).
0 votes
@Michael personne ne se demanderait pourquoi une recherche sur une carte est plus rapide, je dis ça comme ça. Maintenant, si vous parliez de la raison pour laquelle la recherche sur une carte peut parfois être plus lente, cela vaudrait la peine d'en parler. De plus, votre question ne porte pas sur un seul problème, ce qui rend plus difficile l'acceptation d'une réponse unique. Une personne peut vous donner une excellente raison pour laquelle la liste immuable est plus lente et une autre peut vous donner une autre excellente raison pour laquelle une carte est plus rapide, laquelle marquez-vous comme acceptée ? C'est pourquoi vous devez poser la question la plus simple possible.
0 votes
Dans le cas d'Immutable.js, je n'ai pas trouvé de documentation pour m'aider à choisir entre Map et List. Maintenant, j'ai tendance à stocker tous mes ensembles de données en tant que Map en utilisant la clé primaire originale comme clé. Cela me donnerait des recherches de clés 5 fois plus rapides. Y a-t-il des inconvénients ?
0 votes
L'intérêt d'utiliser une carte dans un langage de programmation est de pouvoir effectuer des recherches rapides par clé.
0 votes
Oui, mais Immutable.map est toujours 8 fois plus lent que l'objet js ordinaire, qui est aussi une carte.
0 votes
@Michael Est-ce que votre question est : "Pourquoi ImmutableJS est-il plus lent que ses homologues JavaScript pour les tableaux et les cartes ?" Si oui, ne s'attend-on pas à ce qu'une abstraction utilisant JavaScript soit plus lente que le langage lui-même ?
0 votes
Oui, c'est ça. Mais avec tout le buzz autour de Immutable.js, et les gens qui disent que la différence de performance est négligeable, je pense que 800% est un peu trop élevé. Oh bien J'ai posté ma question parce que j'aurais aimé trouver un article similaire avant de me plonger dans Immutable. Bien sûr, j'aurais dû l'évaluer avant Et je n'aurais probablement pas dû poster ceci sur SE car il s'agit plus d'une discussion que d'une simple question.
0 votes
Je ne comprends pas la confusion. Les abstractions sont presque toujours moins performantes que les opérations de niveau inférieur.
0 votes
@Mardoxx jsperf.com/objet-freeze-vs-immutable/11 . Je sais que vous le remarquerez mais pour être sûr que l'immuableJS n'est pas la seule abstraction dans ce test