ECMAScript 5 strict mód és a teljesítmény
Az ECMAScript 5-ben létrejött strict mód két céllal rendelkezik meglátásom szerint: egyrészt a rossz programozási technikák és véletlen elgépelések ellen véd (lásd: minden változót deklarálni kell, a this
nem lesz automatikusan global scope), másrészt a teljesítményre van jó kihatással. Jelen bejegyzésben az utóbbit szeretném megvizsgálni a jelenleg elérhető strict módot támogató JavaScript motorok vizsgálatával.
A Web Reflections felhevülten írja az [ECMAScript 5] Do Not Remove arguments.callee
! bejegyzésébén 2009 májusában (kiemelések az eredeti cikk szerint):
Introspection and BackTrace? They Decided It Is A Bad Thing!
Without
arguments.callee
we are loosingarguments.callee.caller
as well, often the only way we have to truly debug our code. At the same time the dynamic nature of JavaScript where "you can inject whatever wherever" will loose a big portion of its malleability.Everything For Performances Reason ???
Apparently what this "new" language would like to get rid off is
arguments
variable, probably the most used variable ever, the inspiration of every ArrayLike object or library. This would be for performances, where we need as first step to call 2 functions rather than one for each runtime function assignment (setTimeout
example and every assigned inline lambda) and where everybody will try to overload the language itself providing other methods to emulate the good old stuff![…]
Have fun with ECMAScript 5 and its brilliant strict mode.
Az Updating JavaScript: ES5 előadás (Where's Walden) 11. fóliáján (Strict Mode Effects) pedig ez áll:
No stack introspection via caller/callee/fun.arguments (modern JIT engines cry)
Jelenleg egyetlen JavaScript motor van a világon, ami teljes körűen támogatja a strict módot, ez pedig a BESEN. A honlapja szerint:
Strict code runs faster than non-strict code, for that reason please use preferably"use strict"
where is it possible, because at strict code is the arguments object creation cheaper at funtion calls, for example no setter/getter creation for each function argument in the arguments object.
A Minefieldnek (Firefox 4 kódneve) már csak négy pontot kell teljesítenie a teljes strict mód támogatásához: a this
ből ne legyen global scope (3 pont), és parseInt('08') === 8
(1 pont): mily' öröm, a teljesítménnyel összefüggő részeket már implementálták.
Nézzük, mit mutatnak a száraz tények. Teszt scriptem a Web Worker-ekről szóló cikkben megismert prímszám kereső (azzal a különbséggel, hogy itt azt mérjük, időegység alatt mennyi prímet találtunk).
//"use strict";
function isPrime(num) {
//arguments; // (1)
//arguments.callee; // (2)
//arguments.caller; // (3)
//isPrime.caller; // (4)
//isPrime.arguments; // (5)
for (var i = 2; i <= Math.sqrt(num); i++ ) {
if (num % i == 0) {
return false;
}
}
return true;
}
var num = 123456789, count = 0, start = new Date();
while ( +Date.now() - start < 2000 ) {
if (isPrime(++num)) {
++count;
}
}
alert( count );
Először lefuttatom strict módban, majd strict mód és arguments
elérés, ezután következik a normál mód, ami közben mindig csak egy sort engedélyezek az (1) - (5) közül. Íme, az eredmények (időegység alatt elvégzett számítások száma, a nagyobb a gyorsabb):
Ami ebből látszik, az leginkább az, hogy Minefield esetén teljesítmény szempontjából mindegy, hogy strict vagy normál módban fut a program (ha strict mód kompatibilisan van megírva), a BESEN-nél pedig közel mindegy. (Itt normál módban az arguments
re való hivatkozáskor az egész objektumot létrehozza, ezért tud egy kicsivel gyorsabb lenni a strict mód.)
Az idő majd megmutatja, megérte-e a szabványban rögzíteni ezen, valóban költséges attribútumok hiányát, jelenleg és a közeljövőben talán a legcélszerűbb strict módban kódolni, és normál módban végezni a hibakeresést/futtatást.
Mi a véleményetek?
Utóirat: a cikk megjelenése után Benjamin „BeRo” Rosseaux (a BESEN fejlesztője) felvette velem a kapcsolatot és egy nagyon hasznos tanáccsal látott el: ha az aktuális dátumot szeretném elérni, a +new Date()
helyett használjam a +Date.now()
kifejezést, mert az előbbi esetén minden meghíváskor a garbage collector-nak is dolgoznia kell. És valóban, az új kóddal a 25 000-es értékről rögtön 125 000 fölé emelkedett a BESEN strict mód-beli teljesítménye. (A Minefield alatt is javult az érték kb. 320 ezerről 370 ezerre.) Azonban a kontraszt még kisebb lett a strict módban futó és a strict-kompatibilis, de normál módban futó program között.
Benjamin Rosseaux szerint
Non-strict and strict modes are mostly equal fast in the moment, because the strict-mode codepaths contains mostly still some dummy non-strict-mode stuff, and on the other side, many engines incl. BESEN optimizes the argument object even in the non-strict-mode out (for the function code context creation), if it's not used, so that the performance advantage for to the strict mode will be here very fully minimal in the moment.
Hasznos a strict-mode, de ez
Lehet, hogy sokan fognak ezzel vitatkozni, de: a strict-mode útja nagyban függ a framework fejlesztőktől. Egyre többen használnak keretrendszereket a natív js mellett || helyett (elég csak a JQuery, Prototype, Scriptaculous, Mootools, ExtJS stb. népszerűségére gondolni).
ugyan…
Pedig onnantól még hét hónap kellett, hogy kiadják a szabványt, és több, mint egy év, hogy megjelenjen az első implementáció.
jquery.. és a többiek?
És nem kijelentettem, hogy a keretrendszerek nem fogják támogatni.
Ezzel azt mondtam, hogy mivel már elég jelentős befolyása van a különböző keretrendszereknek, ezért az azokat fejlesztők, indirekt módon befolyásolják a keretrendszer (fel)használókat a strict-mode használatában.
Széljegyzet
"use strict";
jelölés? Ez a javasolt minta?Nem mellékes
Az ECMAScript 5 szabvány 14.1 pontja (111. oldal) szerint az egész JavaScript kód vagy egy függvény legelső utasítása lehet úgynevezett utasítás prológus (Directive Prologue – mi ennek a magyar megfelelője?), ami egy string literal pontosvesszővel lezárva (ez lehet automatikus beszúrással). Ennek megfelelően az Use Strict direktíva a következő kettő valamelyikével pontosan meg kell, hogy egyezzen:
'use strict'
vagy"use strict"
.Oké
Csak egy észrevétel
return
utasításnak egy blokkal kijjebb kellene szerepelni, mert így a for ciklus magja minden függvényhíváskor csak egyszer fut le. Például azisPrime(9)
istrue
értékkel tér vissza, ami persze nem igaz.Javítottam, kösz az