75 votes

Est-ce que la lecture de la propriété `length` d'un tableau est une opération coûteuse en JavaScript?

J'ai toujours supposé que la mise en cache de la longueur d'un tableau en JavaScript est une bonne idée (surtout dans la condition d'un for boucle) en raison de la cherté de calcul de la longueur d'un tableau.

Exemple

for (var i = 0; i < arr.length; i++) { }

// vs

for (var i = 0, arrLength = arr.length; i < arrLength; i++) { }

Cependant, j'ai pensé que peut-être l' length propriété est mise à jour uniquement sur la création et la modification de la matrice. Par conséquent, la lecture, il ne devrait pas être trop cher une opération, par opposition à la lecture il est stocké dans une variable (par opposition à d'autres méthodes dans d'autres langues qui peuvent avoir besoin de chercher dans la mémoire de trouver la fin de quelque chose, par exemple, strlen() en C).

J'ai deux questions. Je suis également intéressé par la façon dont cela fonctionne, donc merci de ne pas me frapper avec le prématuré d'optimisation de bâton.

Assumer les moteurs JavaScript dans les navigateurs.

  1. Est-il un avantage pour la mise en cache de l' length de la propriété d'un tableau en JavaScript? Est-il beaucoup plus impliqués dans la lecture d'une variable locale sur une propriété de l'objet?
  2. Est l' length de la propriété tout simplement modifié sur la création et sur l' shift() et pop() type de méthodes que de ne pas renvoyer un nouveau tableau et sinon simplement stockées sous forme d'un nombre entier?

52voto

KooiInc Points 38845

Eh bien, j'aurais dit que c'était cher, mais ensuite, j'ai écrit un petit test @ jsperf.com et à ma grande surprise, à l'aide de i<array.length fait a été plus rapide dans google Chrome, et dans FF(4) il n'a pas d'importance.

Mon soupçon est que la longueur est considéré comme un entier (Uint32). À partir de l'ECMA-specs (262 ed. 5, page 121):

Chaque Tableau a un objet la longueur de la propriété dont la valeur est toujours un entier positif inférieur à 232. La valeur de la propriété length est numériquement plus élevé que le nom de tous les biens dont le nom est un tableau index; chaque fois qu'une propriété d'un Tableau l'objet est créé ou modifié, les autres les propriétés sont ajustés en fonction des besoins pour maintenir cet invariant. Plus précisément, chaque fois qu'une propriété est ajoutée dont le nom est un tableau d'index, la longueur de la propriété est modifiée, si nécessaire, être un de plus que le la valeur numérique de l'indice de tableau; et lorsque la longueur de la propriété est changé, tous les biens dont le nom est un index de tableau dont la valeur n'est pas plus petite que la nouvelle longueur est automatiquement supprimé. Cette contrainte s'applique uniquement aux propriétés d'un Tableau d'objet et n'est pas affecté par longueur de tableau ou de propriétés d'index peut-être hérité de ses prototypes

Ouf! Je ne sais pas si je l'ai jamais utilisé pour ce genre de langage ...

Enfin, nous avons toujours notre bonne vieille à la traîne navigateur. Dans IE (9, 8, 7) la mise en cache de la longueur est vraiment plus rapide. Un de beaucoup plus de raisons de ne pas utiliser IE, dis-je.

28voto

Demian Brecht Points 11083

TL;DR:

De ce que j'ai pu rassembler, il semble comme la longueur du tableau est mis en cache à l'interne (au moins en V8)..

(Plus de détails? Lire sur :))

Donc, cette question a sonné dans ma tête à quelques reprises et j'ai décidé d'aller à la racine du problème (au moins, dans une mise en œuvre).

Creuser autour V8 source a abouti à la JSArray classe.

// The JSArray describes JavaScript Arrays
//  Such an array can be in one of two modes:
//    - fast, backing storage is a FixedArray and length <= elements.length();
//       Please note: push and pop can be used to grow and shrink the array.
//    - slow, backing storage is a HashTable with numbers as keys.

Je suis en train de faire l'hypothèse que le type des éléments du tableau qui décide si c'est rapide ou lente. Je suis descendu à un indicateur de bit en set_has_fast_elements (set_bit_field2(bit_field2() | (1 << kHasFastElements))), qui est l'endroit où j'ai pensé dessiner le creusement de la ligne que je cherchais sur google code et ne pas avoir la source locale.

Maintenant, il semble que de tout temps, toute l'opération se fait sur le tableau (qui est un enfant de la classe d' JSObject, un appel est fait à NormalizeElements(), qui exécute les opérations suivantes:

// Compute the effective length.
  int length = IsJSArray() ?
      Smi::cast(JSArray::cast(this)->length())->value() :
      array->length();

Ainsi, en répondant à vos questions:

  1. Il ne semble pas être un avantage dans Chrome (ou d'autres navigateurs qui utilisent V8) pour la mise en cache de l' length de la propriété d'un tableau (sauf si vous faites quelque chose de bizarre qui l'obligerait à être slow (je ne suis pas sûr de ce que ces conditions sont) - cela dit, je vais probablement continuer à cache length jusqu'à ce que je reçois une chance de passer à travers tous les OS navigateur implémentations ;)
  2. L' length de la propriété semble être modifiés après toute opération sur l'objet.

Edit:

Sur une note de côté, il semble qu'un "vide" de la matrice est réellement alloués à ont 4 éléments:

// Number of element slots to pre-allocate for an empty array.
static const int kPreallocatedArrayElements = 4;

Je ne suis pas sûr de savoir comment de nombreux éléments de la matrice se développe en une fois les limites ont été dépassées, - je n'ai pas creuser que profond :)

13voto

Anurag Points 66470

Une autre série de tests de performance. La boucle est effectuée sur un tableau de millions de nombres aléatoires avec une boucle vide.

Dans Chrome, les boucles avec des longueurs mises en cache et non mises en cache sont à peu près les mêmes heures. Je suppose donc que l'optimisation V8 permet de mettre en cache la longueur.

Dans Safari et Firefox, la longueur de la mise en cache était systématiquement environ deux fois plus rapide que celle de la version non mise en cache.

4voto

AMorrise Points 2088

Juste une note:

Sur certains navigateurs (je l'ai remarqué dans Safari, IE et Opera), vous pouvez augmenter la vitesse en mettant en cache la longueur dans la déclaration de boucle for:

 var j;
for (var i = 0, len = arr.length; i < len; i++) {
  j = arr[i];
}
 

J'ai modifié le test jsperf de @ KooiInc ci-dessus pour ajouter ce cas .

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X