Peut-être parce que les Jeux sont relativement nouvelles pour Javascript, mais je n'ai pas été en mesure de trouver un article, sur StackO ou n'importe où ailleurs, qui parle de la différence de performances entre les deux en Javascript. Alors, quelle est la différence, en termes de performance, entre les deux? Plus précisément, lorsqu'il s'agit de suppression, d'ajout et d'itération.
Réponses
Trop de publicités?Ok, j'ai testé l'ajout, l'itération et la suppression d'éléments d'un tableau et d'un ensemble. J'ai couru un "petit" test, à l'aide de 10 000 éléments et une "grande" de test, l'utilisation de 100 000 éléments. Voici les résultats.
Ajout d'éléments à une collection
Il semblerait que l' .push
méthode de tableau est environ 4 fois plus rapide que l' .add
méthode de jeu, peu importe le nombre d'éléments ajoutés.
Parcourir et modifier des éléments dans une collection
Pour cette partie du test, j'ai utilisé un for
boucle pour parcourir le tableau et un for of
boucle pour parcourir l'ensemble. De nouveau, parcourir le tableau a été plus rapide. Cette fois, il semble que c'est de façon exponentielle, de sorte qu'il a fallu deux fois plus longtemps pendant les "petites" les tests et près de quatre fois plus de temps pendant les "grands" des tests.
La suppression d'éléments d'une collection
Maintenant, c'est là que ça devient intéressant. J'ai utilisé une combinaison d'un for
boucle et .splice
de supprimer certains éléments de la matrice et j'ai utilisé for of
et .delete
de supprimer certains éléments de l'ensemble. Pour les "petits" tests, il a été environ trois fois plus rapide pour supprimer des éléments de l'ensemble (2,6 ms vs 7.1 ms), mais les choses ont changé de façon drastique pour les "grands" test où il a pris 1955.1 ms pour supprimer des éléments de la matrice tout cela a seulement pris de 83,6 ms pour les supprimer, 23 fois plus rapide.
Conclusions
À 10k éléments, les deux tests ont couru des périodes comparables (tableau: 16.6 ms, set: 20.7 ms), mais lorsque l'on traite avec 100k éléments, le set a été le gagnant clair (tableau: 1974.8 ms, set: 83.6 ms), mais seulement en raison de la suppression de l'opération. Sinon, le tableau a été plus rapide. Je ne pourrais pas dire exactement pourquoi.
J'ai joué un peu avec quelques hybrides scénarios où un tableau est créé et rempli, puis converti en un ensemble où certains éléments serait supprimé, l'ensemble serait alors de se reconvertir dans un tableau. Bien que cela va donner beaucoup de meilleures performances que la suppression d'éléments dans le tableau, le supplément de temps de traitement requis pour le transfert vers et à partir d'un ensemble emportent sur les gains de remplir un tableau au lieu d'un jeu. En fin de compte, il est plus rapide de ne faire affaire qu'avec un ensemble. Pourtant, c'est une idée intéressante, que si l'on choisit d'utiliser un tableau de la collecte de données pour certains gros des données qui n'ont pas de doublons, il pourrait être avantageux de performance sage, si il n'y a jamais un besoin de supprimer de nombreux éléments en une seule opération, pour convertir le tableau d'ensemble, effectuer l'opération de suppression, et de convertir le retour à un tableau.
Tableau code:
var timer = function(name) {
var start = new Date();
return {
stop: function() {
var end = new Date();
var time = end.getTime() - start.getTime();
console.log('Timer:', name, 'finished in', time, 'ms');
}
}
};
var getRandom = function(min, max) {
return Math.random() * (max - min) + min;
};
var lastNames = ['SMITH', 'JOHNSON', 'WILLIAMS', 'JONES', 'BROWN', 'DAVIS', 'MILLER', 'WILSON', 'MOORE', 'TAYLOR', 'ANDERSON', 'THOMAS'];
var genLastName = function() {
var index = Math.round(getRandom(0, lastNames.length - 1));
return lastNames[index];
};
var sex = ["Male", "Female"];
var genSex = function() {
var index = Math.round(getRandom(0, sex.length - 1));
return sex[index];
};
var Person = function() {
this.name = genLastName();
this.age = Math.round(getRandom(0, 100))
this.sex = "Male"
};
var genPersons = function() {
for (var i = 0; i < 100000; i++)
personArray.push(new Person());
};
var changeSex = function() {
for (var i = 0; i < personArray.length; i++) {
personArray[i].sex = genSex();
}
};
var deleteMale = function() {
for (var i = 0; i < personArray.length; i++) {
if (personArray[i].sex === "Male") {
personArray.splice(i, 1)
i--
}
}
};
var t = timer("Array");
var personArray = [];
genPersons();
changeSex();
deleteMale();
t.stop();
console.log("Done! There are " + personArray.length + " persons.")
Définir un code:
var timer = function(name) {
var start = new Date();
return {
stop: function() {
var end = new Date();
var time = end.getTime() - start.getTime();
console.log('Timer:', name, 'finished in', time, 'ms');
}
}
};
var getRandom = function (min, max) {
return Math.random() * (max - min) + min;
};
var lastNames = ['SMITH','JOHNSON','WILLIAMS','JONES','BROWN','DAVIS','MILLER','WILSON','MOORE','TAYLOR','ANDERSON','THOMAS'];
var genLastName = function() {
var index = Math.round(getRandom(0, lastNames.length - 1));
return lastNames[index];
};
var sex = ["Male", "Female"];
var genSex = function() {
var index = Math.round(getRandom(0, sex.length - 1));
return sex[index];
};
var Person = function() {
this.name = genLastName();
this.age = Math.round(getRandom(0,100))
this.sex = "Male"
};
var genPersons = function() {
for (var i = 0; i < 100000; i++)
personSet.add(new Person());
};
var changeSex = function() {
for (var key of personSet) {
key.sex = genSex();
}
};
var deleteMale = function() {
for (var key of personSet) {
if (key.sex === "Male") {
personSet.delete(key)
}
}
};
var t = timer("Set");
var personSet = new Set();
genPersons();
changeSex();
deleteMale();
t.stop();
console.log("Done! There are " + personSet.size + " persons.")
OBSERVATIONS:
- Ensemble des opérations peut être compris comme des instantanés dans le flux d'exécution.
- Nous ne sommes pas devant un définitif de substitution.
- Les éléments d'un Ensemble de classe ont pas accessible index.
- Ensemble de la classe est une classe Array complément, utile dans les scénarios où nous avons besoin de stocker une collection sur laquelle appliquer ajout de base, La suppression, la vérification et l'itération des opérations.
Je partage certains de test de performance. Essayez d'ouvrir votre console et copypaste le code ci-dessous.
La création d'un tableau (125000)
var n = 125000; var arr = Array.apply( null, Array( n ) ).map( ( x, i ) => i ); console.info( arr.length ); // 125000
1. La localisation d'un Index
Nous avons comparé la méthode de Régler avec le Tableau indexOf:
Tableau/indexOf (0.281 ms) | Set/a (0.053 ms)
// Helpers var checkArr = ( arr, item ) => arr.indexOf( item ) !== -1; var checkSet = ( set, item ) => set.has( item ); // Vars var set, result; console.time( 'timeTest' ); result = checkArr( arr, 123123 ); console.timeEnd( 'timeTest' ); set = new Set( arr ); console.time( 'timeTest' ); checkSet( set, 123123 ); console.timeEnd( 'timeTest' );
2. L'ajout d'un nouvel élément
Nous comparons les ajouter et de pousser les méthodes de la série de tableaux et d'objets:
Tableau/push (1.612 ms) | Set/ajouter (0.006 ms)
console.time( 'timeTest' ); arr.push( n + 1 ); console.timeEnd( 'timeTest' ); set = new Set( arr ); console.time( 'timeTest' ); set.add( n + 1 ); console.timeEnd( 'timeTest' ); console.info( arr.length ); // 125001 console.info( set.size ); // 125001
3. La suppression d'un élément
Lors de la suppression d'éléments, nous devons garder à l'esprit que le Tableau et le Jeu ne démarre pas dans des conditions d'égalité. Tableau n'est pas une méthode native, donc une fonction externe est nécessaire.
Tableau/deleteFromArr (0.356 ms) | Définir/supprimer (0.019 ms)
var deleteFromArr = ( arr, item ) => { var i = arr.indexOf( item ); i !== -1 && arr.splice( i, 1 ); }; console.time( 'timeTest' ); deleteFromArr( arr, 123123 ); console.timeEnd( 'timeTest' ); set = new Set( arr ); console.time( 'timeTest' ); set.delete( 123123 ); console.timeEnd( 'timeTest' );
Lire l'article complet ici
J'ai récemment rencontré ce test et a constaté que beaucoup surperformé un Tableau de 1000 articles (environ 10 fois plus d'opérations sont susceptibles de se produire dans la même période). Et selon le navigateur soit battu ou perdu un Objet.hasOwnProperty dans l'un comme pour l'essai.
Les deux Ensemble et de l'Objet, ont leur "a la" méthode de la scène dans ce qui semble être amorti O(1), mais en fonction du navigateur de mise en œuvre d'une seule opération pourrait prendre plus de temps ou plus.
https://jsperf.com/set-has-vs-object-hasownproperty-vs-array-includes/1 Dans le cas où vous souhaitez exécuter vos propres tests avec les différents navigateurs et des milieux.
Mon observation est que un Jeu est toujours mieux à deux écueils pour les grands tableaux à l'esprit :
a) La création d'Ensembles de Matrices doivent être réalisées en for
boucle avec une mises en cache au préalable la longueur.
lente (par exemple, 18ms) new Set(largeArray)
rapide (par exemple, 6ms)
const SET = new Set();
const L = largeArray.length;
for(var i = 0; i<L; i++) { SET.add(largeArray[i]) }
b) l'Itération peut être effectuée de la même façon, parce qu'il est aussi rapide qu'un for of
boucle ...
Voir https://jsfiddle.net/0j2gkae7/5/
pour une vraie vie de comparaison pour
difference()
, intersection()
, union()
et uniq()
( + leurs iteratee compagnons etc.) avec 40.000 éléments
console.time("set")
var s = new Set()
for(var i = 0; i < 10000; i++)
s.add(Math.random())
s.forEach(function(e){
s.delete(e)
})
console.timeEnd("set")
console.time("array")
var s = new Array()
for(var i = 0; i < 10000; i++)
s.push(Math.random())
s.forEach(function(e,i){
s.splice(i)
})
console.timeEnd("array")
Ces trois opérations sur 10K éléments m'a donné:
set: 7.787ms
array: 2.388ms