Autor Zpráva
Suta
Profil
Mrzí mě, že ve spoustě kódu jde framework Prototype.js svojí cestou na úkor optimalizace.

Udělal jsem několik testů metod, které používám ve vlastní knihovně a metod, jež implementuje Prototype.js - výsledky nevrhají na tento framework správné světlo. Jako příklad uvedu metodu lastIndexOf(), jak je implementována nativně v některých prohlížečích (a kterou využívám já), a jak ji implementuje prototype.js.

Zde jsou zprůměrované výsledky při zavolání metody v cyklu 100.000 krát za sebou:

Metoda lastIndexOf() - srovnání verze aplikované v Prototype.js a "standardní" metody:

Spuštěno 100.000krát v cyklu.

Prototype.js (čas v levém sloupci) versus standardní přístup (čas v pravém sloupci):

IE6: 2.925 s / 0.769 s
IE7: 2.796 s / 0.798 s
Safari: 0.212 s / 0.039 s
Opera: 0.188 s / 0.031 s - metoda v prototype.js je 5x pomalejší
IE8: 0.132 s / 0.088 s
Chrome: 0.098 s / 0.038 s
Firefox: 0.051 s / 0.023 s



A tady jsou ony dvě metody.

Nejprve metoda lastIndexOf, jak je implementována v Prototype.js:

  Array.prototype.lastIndexOf = function(searchElement, fromIndex) {

    fromIndex = isNaN(fromIndex)
        ? this.length
        : (fromIndex < 0
            ? this.length + fromIndex
            : fromIndex) + 1;

        var n = this.slice(0, fromIndex).reverse().indexOf(searchElement); // rád bych znal motivaci Sama Stephensona pro tento postup
        return (n < 0) ? n : fromIndex - n - 1;
  }


Metoda lastIndexOf naprogramována odlišně ("standardně"):

  Array.prototype.lastIndexOf = function(searchElement, fromIndex) {

      var length = this.length;

      fromIndex = Number(fromIndex);

      if (isNaN(fromIndex)) {
          fromIndex = length - 1;
      }
      else {
          fromIndex = (fromIndex < 0)
              ? Math.ceil(fromIndex)
              : Math.floor(fromIndex);

          if (fromIndex < 0) fromIndex += length;

          else if (fromIndex >= length) fromIndex = length - 1;
      }

      for (; fromIndex > -1; fromIndex--) {
          if (fromIndex in this && this[fromIndex] === searchElement)
          return fromIndex;
      }
      return -1;
};



Samozřejmě jde o přehnaný cyklus. Na druhé straně se zase ale jedná o jednoduché zavolání a aplikování na pole s pěti prvky, při použití složitějších skriptů a řetězení už můžeme počítat na desetiny. Nicméně chtěl jsem poukázat na to, že i ve "velkých" knihovnách jsou často pěkné nesmysly. A tato metoda není v Prototype.js jediná. Takových, co jsem testoval pro optimalizaci výkonu je tam spousta. Bohužel.

Mrzí mě i to, že Prototype.js netestuje přítomnost nativních metod. Např. Chrome již má metodu lastIndexOf() vestavěnu nativně, a běží opět o něco rychleji než mnou uvedená funkce.

I od této cesty (viz níže) Prototype.js upouští. Jaká škoda.

if (!Array.prototype.lastIndexOf) {
        Array.prototype.lastIndexOf = function(searchElement, fromIndex) {
        // kód metody
  }
}

joe
Profil
Mrzet tě to může a nejen tebe, ale nebylo by lepší to psát na fórum o Prototype frameworku, než sem, kde s tím asi nic neuděláme (já určitě ne)?
Witiko
Profil
joe:
kde s tím asi nic neuděláme (já určitě ne)
Myslím, že nahradit si definici funkce v kódu frameworku je poměrně triviální postup. Divil bych se, kdyby tak Suta v případě, že by framework používal, neučinil. Spousta lidí zde včetně mě určitě shledává tyto postřehy zajímavými (ačkoliv ne překvapivými). S jediným bych souhlasil a sice nahlásit problémovost na fóru Prototype. A nebo se na danou knihovnu vykašlat.

Suta:
Mrzí mě i to, že Prototype.js netestuje přítomnost nativních metod.
To je ovšem u frameworku dvojsečná zbraň. Framework by totiž nadevše jiné měl kódování abstraktizovat - tedy učinit kód nezávislý na platformě -> stejně funkční na všech prohlížečích. Z tohoto důvodu má tento tzv. Dual Native-first Approach, kdy je upřednostňována předdefinovaná funkce před vnitřní, 2 vady:

1) Použitá funkce nemusí být nativní, tzn. někdo ji může před spuštěním scriptu předefinovat.
Ale hlavně:
2) Použitím nativní funkce dochází k porušení abstraktizace. Nativní a hlavně výše-úrovňové funkce jsou černá skříňka, nikdy člověk neví, co navrátí a ani nemůže navrácené výsledky nijak kontrolovat, protože tím by došlo k odstranění jediného pozitiva této metody, jímž je rychlost.

Názory na DNFA se liší, všeobecně se dá říct, že u nízkoúrovňových funkcí (jako lastIndexOf), kde je šance problémů a chyb minimální, se většinou používá, zatímco u funkcí na vyšší úrovni, u kterých je větší náchylnost na rozdílné výsledky u různých prohlížečů, by se dle mnoha lidí nativní funkce používat neměl.

Např. jQuery je už nějakou dobu terčem kritiky za využívání DNFA právě pro vyhledávání podle CSS3 identifikátoru. Obsahuje sice knihovnu Sizzle, která nativní vyhledávání simuluje, nicméně v případě podpory nativních metod použije ty. Rychlostně jde možná o pokrok co do časů vyhledávání, avšak míra abstraktizace je na prohlížečích s podporou document.querySelectorAll nulová. I díky tomu spousta lidí upírá jQuery titul frameworku.
Suta
Profil
Witiko:
Nemůžu, než souhlasit, vystihl jsi to naprosto přesně a vzal mi vše, co jsem chtěl k tématu dodat.

Zároveň musím ospravedlnit svůj omyl, konkrétně u metod indexOf a lastIndexOf Prototype.js jejich nativní přítomnost testuje (ve verzi 1.7 na řádku 1219).

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: