Autor Zpráva
Witiko
Profil
Psal jsem si pro vlastní potřeby funkci Array.prototype.each a narazil jsem přitom na zvláštní fenomén. Zdá se, že tato mnou definovaná funkce projde polem mnohem rychleji, než normální for cyklus. Mnohonásobně. Očekával bych spíše opačný výsledek, vzhledem k tomu, že funkce sama for cyklu využívá, dokáže toto chování někdo rozumně odůvodnit?

Test rychlosti:
/* Definice mé [].each() funkce a zpětná kompatibilita pro Date.now() */

Array.prototype.each = function(callback) {
  if(typeof callback != "function" || !this.length) return this;
  for(var index = 0; index < this.length; index++) {
    if(callback.call(this, this[index], index) == false) break;
  }
  return this;
}

if(!Date.now)
  Date.now = function() {
    return new Date().getTime();
  }
  
/* Vytvoření pole */

var pole = [];
while(pole.length < 1000000) {
  pole.push(Math.random());
}

/* Měření rychlosti normálního cyklu */

var time = [], počítadlo = 0;
time[0] = Date.now();

for(var index = 0; index < pole.length; index++) {
  počítadlo += pole[index];
}

time[0] = (Date.now() - time[0]) / 1000;

/* Měření rychlosti za použití funkce .each() */

počítadlo = 0;
time[1] = Date.now();

pole.each(function(číslo) {
  počítadlo += číslo;
});

time[1] = (Date.now() - time[1]) / 1000;

/* Cílová rovinka */

window.alert("Cyklus for: " + time[0] + "s\n"  /* Výsledek v Chrome: 1.975s */
           + "Funkce .each: " + time[1] + "s"); /* Výsledek v Chrome: 0.435s */
ShiraNai7
Profil
Zkus to spustit mimo globalni scope.. treba ve funkci (viz nize). A rozdil? Cyklus for jen 0.138s a tvoje .each 1.034s. Pri volani v globalnim scope bylo .each rychlejsi (prave protoze je to for az ve funkci mimo globalni scope). A duvod? Uprimne nevim... mozna protoze objekt window ke kteremu jsou vazany promenne v onom "globalnim scope", ma spoustu promennych nebo je tam nejaka extra funkcionalita :D

(function(){

/* Definice mé [].each() funkce a zpětná kompatibilita pro Date.now() */

Array.prototype.each = function(callback) {
  if(typeof callback != "function" || !this.length) return this;
  for(var index = 0; index < this.length; index++) {
    if(callback.call(this, this[index], index) == false) break;
  }
  return this;
}

if(!Date.now)
  Date.now = function() {
    return new Date().getTime();
  }
  
/* Vytvoření pole */

var pole = [];
while(pole.length < 1000000) {
  pole.push(Math.random());
}

/* Měření rychlosti normálního cyklu */

var time = [], počítadlo = 0;
time[0] = Date.now();

for(var index = 0; index < pole.length; index++) {
  počítadlo += pole[index];
}

time[0] = (Date.now() - time[0]) / 1000;

/* Měření rychlosti za použití funkce .each() */

počítadlo = 0;
time[1] = Date.now();

pole.each(function(číslo) {
  počítadlo += číslo;
});

time[1] = (Date.now() - time[1]) / 1000;

/* Cílová rovinka */

window.alert("Cyklus for: " + time[0] + "s\n"  /* Výsledek v Chrome: 1.975s */
           + "Funkce .each: " + time[1] + "s"); /* Výsledek v Chrome: 0.435s */

})();
_es
Profil
Witiko:
Cyklus for máš spravený tak, že pracuje s globálnymi premennými, ku ktorým musí pristupovať tri krát.
S lokálnymi premennými by to bolo rýchlejšie.
Je zbytočne zisťovaná vlastnosť length pri každej iterácii.
Tú rekurziu v metóde each som nejako nepochopil, nespraví náhodou tá metóda menej iterácií?
ShiraNai7 ma stihol predbehnúť.
Witiko
Profil
_es:
Tú rekurziu v metóde each som nejako nepochopil, nespraví náhodou tá metóda menej iterácií?
callback.call(this, this[index], index) - tímto předávám dané funkci v this odkaz na pole
if(callback.call(this, this[index], index) == false) break - tímto umožňuji navrácením false funkci kdykoliv breaknout.

Takže pak ve výsledku mi bude fungovat například tento kód:
var pole = [1, 2, 3, 4].each(function() {
  this.reverse();
  return false;
});
// pole == [4, 3, 2, 1]


Samozřejmě daný kód použít nehodlám, jde jen o ukázku. Často se možnost breaknout procházení pole hodí a nechtěl jsem, abych kvůli tomu musel přesidlovat zpět k for.

Je zbytočne zisťovaná vlastnosť length pri každej iterácii.
Jak bys tedy potom ten cyklus zapsal?

ShiraNai7:
Pri volani v globalnim scope bylo .each rychlejsi
Zajímavé. Zajímalo by mě, proč je přístup ke globálním proměnným pomalejší.
ShiraNai7
Profil
Witiko:
Zajímavé. Zajímalo by mě, proč je přístup ke globálním proměnným pomalejší.

Global variables have slow performance because they live in a highly-populated namespace. Not only are they stored along with many other user-defined quantities and JavaScript variables, the browser must also distinguish between global variables and properties of objects that are in the current context. Many objects in the current context can be referred to by a variable name rather than as an object property, such as alert() being synonymous with window.alert(). The down side is this convenience slows down code that uses global variables.

Viz http://www.webreference.com/programming/javascript/jkm3/index.html část Technique 1: Use local function variables
_es
Profil
Witiko:
Jak bys tedy potom ten cyklus zapsal?
Z hľadiska stručnosti stačí while:
(function() {
  var p = pole, s = 0, i = p.length;
  while(i--) s += p[i];
  počítadlo = s;
})();
Witiko
Profil
_es:
Ano, a v případě, že by mi šlo o pořadí v jakém budu pole procházet?
_es
Profil
(function() {
  var p = pole, s = 0, i = 0, m = p.length;
  while(i !== m) s += p[i++];
  počítadlo = s;
})();
Witiko
Profil
_es:
Můžu se zeptat, proč je var c = 0; var a = b.length; while(c < a) {} lepší přístup, nežli var c = 0; while(c < b.length) {}? Měl jsem za to, že rozdíl čtením atributu objektu obsahujícím základní datový typ a proměnné obsahující základní datový typ by neměl být nějaký obzvlášť velký rozdíl. Nebo se velikost pole při každém dožádání vlastnosti .length přepočítává?
_es
Profil
Witiko:
proč je var c = 0; var a = b.length; while(c < a) {} lepší přístup, nežli var c = 0; while(c < b.length) {}
Lebo veľkosť poľa sa pri každom teste musí zisťovať. Rozdiel je veľmi malý, no pri veľa iteráciách to môže zavážiť.
Ešte rýchlejšie než while(c < a) je while(c !== a), lebo zistenie neidentity je menej náročné, ako zistenie, ktorý z členov je väčší.
Chamurappi
Profil
Reaguji na _es:
Lebo veľkosť poľa sa pri každom teste musí zisťovať.
Ve specifikaci ECMAScriptu se v kapitole „15.4 Array Objects“ píše:
The value of the length property is numerically greater than the name of every property whose name is an array index; whenever a property of an Array object is created or changed, other properties are adjusted as necessary to maintain this invariant. Specifically, whenever a property is added whose name is an array index, the length property is changed, if necessary, to be one more than the numeric value of that array index; and whenever the length property is changed, every property whose name is an array index whose value is not smaller than the new length is automatically deleted.
… z čehož by se dalo usuzovat, že se length v paměti samovolně mění již při úpravách pole a při čtení se již neděje zjišťování. Možná to u polí není implementačně tak nešikovné jako u (živých) Nodelistů.
Witiko
Profil
Chamurappi:
z čehož by se dalo usuzovat, že se length v paměti samovolně mění
Přesně mé myšlenky, pole není živý odkaz na DOM a length hodnota se mění pouze při změnách v poli. Záleží samozřejmě na implementaci, ale neviděl bych zde nutnost přepočítávání délky pole při zjišťování vlastnosti length.

Každopádně strukturu each() funkce zachovám z jednoho prostého důvodu - při průchodu polem může být z připojené funkce pole měněno. Kontrola indexu při každém průchodu je tudíž nutná.

_es
while(c < a) je while(c !== a)
Můžu se zeptat odkud čerpáš? Ne, že bych ti nevěřil, jen by mě zajímalo, je-li možné se o tom dočíst, nebo jestli jde jen o logickou dedukci.

Mimochodem: Je tedy a === b také méně náročné, než a == b? Pamatuji si, jak někdo kdysi kritizoval kód jQuery, že je v něm přespříliš striktních porovnání a sám většinou se snažím používat méně striktní než striktní porovnávání, tak mě to zajímá.
_es
Profil
Chamurappi:
z čehož by se dalo usuzovat, že se length v paměti samovolně mění již při úpravách pole a při čtení se již neděje zjišťování.
Ak aj je length pri zisťovaní statické číslo, stále to bude o trochu pomalšie, je to prístup k vlastnosti objektu.

Witiko:
Je tedy a === b také méně náročné, než a == b?
Záleží od toho, čo je a a čo je b. Ak to sú dve čísla, mohlo by to byť to isté, no možno to bude v každom prehliadači iné. Operátor == v princípe nemôže byť menej náročný ako operátor ===.

Pamatuji si, jak někdo kdysi kritizoval kód jQuery, že je v něm přespříliš striktních porovnání
To kritizoval zle.

a sám většinou se snažím používat méně striktní než striktní porovnávání
Okrem možného teoretického nevýznamného spomalenia hrozia pri tých operátoroch nepríjemné, ťažko odhaliteľné, chyby v dôsledku automatických pretypovaní operandov. Pravidlá toho pretypovania nie sú až tak jednoduché. Najlepšie je uprednostňovať striktné operátory a menej striktné použiť až vtedy, ak sa tie automatické pretypovania hodia.

Vaše odpověď

Mohlo by se hodit

Neumíte-li správně určit příčinu chyby, vkládejte odkazy na živé ukázky.
Užíváte-li nějakou cizí knihovnu, ukažte odpovídajícím, kde jste ji vzali.

Užitečné odkazy:

Prosím používejte diakritiku a interpunkci.

Ochrana proti spamu. Napište prosím číslo dvě-sta čtyřicet-sedm:

0