Javascript Performance
description
Transcript of Javascript Performance
JavaScript OptimizationDos and Don’ts
Florian Loitsch<[email protected]>
July 14, 2010
Florian Loitsch JavaScript Optimization July 14, 2010 1 / 51
1 IntroductionJavaScript
2 When not to Optimize
3 XHR and DOM
4 Don’ts
5 Miscellaneous
6 Final Words
Florian Loitsch JavaScript Optimization July 14, 2010 2 / 51
Outline
1 IntroductionJavaScript
2 When not to Optimize
3 XHR and DOM
4 Don’ts
5 Miscellaneous
6 Final Words
Powerful
• JavaScript has matured.
• Powerful programs possible. (Try Gmail in an old browser.)
• Visually interesting demos.• http://chromeexperiments.com
Florian Loitsch JavaScript Optimization July 14, 2010 4 / 51
Wait
Best way to speed up a JavaScript program is to wait.
• Engines become faster and faster.
• Even MS is catching up.
• Computers are getting faster.
Florian Loitsch JavaScript Optimization July 14, 2010 5 / 51
Outline
1 IntroductionJavaScript
2 When not to Optimize
3 XHR and DOM
4 Don’ts
5 Miscellaneous
6 Final Words
Florian Loitsch JavaScript Optimization July 14, 2010 6 / 51
Description
JavaScript is a dynamic functional language with C-like syntax.
• Dynamic typing.
• Functions are first class citizens.
• Closures.
• Automatic garbage-collection.
• Dynamic objects. Fields may be removed/added after construction.
• Prototype inheritance.
• Only few native types.
Florian Loitsch JavaScript Optimization July 14, 2010 7 / 51
Dynamically Typed
Variables don’t have types. Thy may receive values of different types overtheir lifetime.
v a r x = 3 ;x = ” s t r i n g ” ;
Florian Loitsch JavaScript Optimization July 14, 2010 8 / 51
Functions
JavaScript provides closures, which are first class citizens.
• Functions can be stored in local variables, or be used as parameters.
• Functions may be created anywhere.
• Functions may nest.
• Functions capture their environment.
v a r x = 1 ;f u n c t i o n f oo ( y ) {
r e t u r n f u n c t i o n ( ) { a l e r t ( x + ” ” + y ) ; }}v a r f 1 = foo ( 2 ) ;v a r f 2 = foo ( 3 ) ;f 1 ( ) ; // => 1 2
f 2 ( ) ; // => 1 3
x++;f1 ( ) ; // => 2 2
f 2 ( ) ; // => 2 3
Florian Loitsch JavaScript Optimization July 14, 2010 9 / 51
Objects - Creation
JavaScript objects can be created in two ways:
1 object literals.
v a r o = { x : 1 , y : 2 } ;
2 applying new on functions.
f u n c t i o n A( x , y ) {t h i s . x = x ;t h i s . y = y ;
}v a r a = new A(1 , 2 ) ;
Florian Loitsch JavaScript Optimization July 14, 2010 10 / 51
Objects - Dictionary
• JavaScript have named properties, mapping names (strings) to values.
• Properties (JavaScript’s name for fields) can be added/removeddynamically.
v a r o = {} ; // empty object
o . x = 3 ;a l e r t ( o . x ) ; // => 3
a l e r t ( o [ ’ x ’ ] ) ; // same as o.x => 3
o . x = 4 ;a l e r t ( o . x ) ; // => 4
d e l e t e ( o . x ) ;a l e r t ( o . x ) ; // => undefined
o [ ’ any s t r i n g i s v a l i d ’ ] = 0 ;
Florian Loitsch JavaScript Optimization July 14, 2010 11 / 51
Objects - Inheritance
Prototype based inheritance.
• Objects have other objects as prototypes.
• Reading: if a property can’t be found in an object, the searchcontinues recursively on its prototype.
• Writing: always executed on the object itself (even if the propertydidn’t exist yet).
Object . p r o t o t yp e . x = 3 ;v a r o = new Object ( ) ;a l e r t ( o . x ) ; // => 3
Object . p r o t o t yp e . x = 4 ;a l e r t ( o . x ) ; // => 4
o . x = 5 ;a l e r t ( o . x ) ; // => 5
a l e r t ( Object . p r o t o t yp e . x ) ; // => 4
v a r o2 = new Object ( ) ;a l e r t ( o2 . x ) ; // => 4
Florian Loitsch JavaScript Optimization July 14, 2010 12 / 51
Arrays
• Arrays are objects with length property.
v a r a = [ ] ; // empty array.
a [ 0 ] = 1 ;a l e r t ( a . l e n g t h ) ; // => 1
a [ 3 ] = 2 ;a l e r t ( a . l e n g t h ) ; // => 4
a l e r t ( a [ ’ 3 ’ ] ) ; // => 2
a [ ’ 1 0 ’ ] = 42 ;a l e r t ( a . l e n g t h ) ; // => 11
a . foo = ’ JS ’ ;a l e r t ( a . l e n g t h ) ; // => 11
Florian Loitsch JavaScript Optimization July 14, 2010 13 / 51
Types
Only few native types:
• No characters. (1 character strings).
• No integers. (Doubles).
Florian Loitsch JavaScript Optimization July 14, 2010 14 / 51
Outline
1 IntroductionJavaScript
2 When not to Optimize
3 XHR and DOM
4 Don’ts
5 Miscellaneous
6 Final Words
90/10
• 90% of the time is spent in 10% of the code.
• premature optimization is the root of all evil - Donald Knuth
Use profilers.Comment on the Chromium Blog:
Profiling is really easy to use.Simple and straightforward, I like it.
Florian Loitsch JavaScript Optimization July 14, 2010 16 / 51
GWT
GWT: a Java-to-JavaScript compiler.
• Let others (Google) do the optimizations for you.
• Easy to optimize for JITs (few dynamic features).
• Not JavaScript.
Florian Loitsch JavaScript Optimization July 14, 2010 17 / 51
Chrome Frame
Internet Explorer.
• IE is slow (IE9 should be better).
• Old versions very common. Companies have difficulties to get rid ofthem.
Easy solution to bring IE up to speed: Chrome Frame plugin.
• Chrome rendering and JavaScript engine inside IE.
• Only active when requested by web page.
• Best of both worlds. Compatibility and speed.
Florian Loitsch JavaScript Optimization July 14, 2010 18 / 51
Compilers are Complex Beasts
Difficult to know what compilers (and JITs) do.
• Many hand-optimizations useless.
• What optimizations improve performance?• Inline functions or constants?• Replace x = x + 1 with x++?• ...
Florian Loitsch JavaScript Optimization July 14, 2010 19 / 51
Anecdotes
On old Firefox versions (FF2.5):
• x++ faster than x = x + 1.
• Additional try/catch sometimes speeds up code.
On Rhino: wrapping a program into an anonymous function slows downthe program.
1 f u n c t i o n f oo ( ) {2 . . .3 }4 f u n c t i o n bar ( ) {5 . . . f oo ( ) . . .6 }
1 ( f u n c t i o n ( ) {2 f u n c t i o n f oo ( ) {3 . . .4 }5 f u n c t i o n bar ( ) {6 . . . f oo ( ) . . .7 }8 } ) ( ) ;
On all browsers: inlining speeds up code.
Florian Loitsch JavaScript Optimization July 14, 2010 20 / 51
Anecdotes cont.
Things that do not matter anymore on Chrome:
• Using parseInt instead of Math.floor or |0 (bit-or). Still a verybad idea (what about 1e100?).
• Using [] on strings when charCodeAt would do. These days wedon’t create a 1-character string.
• Using %4 where &3 would have done. Not much slower any more.
• Using arguments. We optimize the simple cases away now; includingusing it in apply. Still a bad idea.
• Using repeated plus on strings instead of join. We have cons strings.
Florian Loitsch JavaScript Optimization July 14, 2010 21 / 51
Compilers are Complex Beasts, cont.
• JavaScript engines are unpredictable.
• Different types of JITs.
• Changes over (short) time.
• Readability first.
• If necessary profile and optimize.
• Profile on all platforms.
Florian Loitsch JavaScript Optimization July 14, 2010 22 / 51
Compilers are Complex Beasts, cont.
• JavaScript engines are unpredictable.
• Different types of JITs.
• Changes over (short) time.
• Readability first.
• If necessary profile and optimize.
• Profile on all platforms.
Florian Loitsch JavaScript Optimization July 14, 2010 22 / 51
Outline
1 IntroductionJavaScript
2 When not to Optimize
3 XHR and DOM
4 Don’ts
5 Miscellaneous
6 Final Words
Typically Expensive Operations
Use profilers, but the following culprits tend to show up frequently.
• XmlHttpRequests (XHRs): a round-trip to server costs time.
• getElementById, and DOM interactions in general.
• Filtering for elements of a specific type and attaching event-handlersto all of them.
Thanks to Olivier Percebois-Garve from Amadeus for these real-worldexamples.
Florian Loitsch JavaScript Optimization July 14, 2010 24 / 51
XmlHttpRequests
• A round-trip to the server costs around 30ms.
• XHRs can be sent in parallel.
Florian Loitsch JavaScript Optimization July 14, 2010 25 / 51
getElementById
• getElementById is a DOM function.
• DOM functions are not native to JavaScript and switches (V8 toWebKit) take time.
1 window . x = 42 ;2 f u n c t i o n f oo ( ) {3 f o r ( v a r i = 0 ;4 i < 10000000;5 i++) {6 window . x++;7 }8 }
1 window . x = 42 ;2 f u n c t i o n f oo ( ) {3 v a r x = window . x ;4 f o r ( v a r i = 0 ;5 i < 10000000;6 i++) {7 x++;8 }9 window . x = x ;
10 }
• window is part of DOM.
• Right version is 75 times faster.
Florian Loitsch JavaScript Optimization July 14, 2010 26 / 51
getElementById cont.
• getElementById can usually be cached.
• Example:
1 v a r cached f oo = unde f i n ed ;2 $foo = f u n c t i o n ( ){3 i f ( c a ched f oo ) {4 cached f oo = document . getE lementBy Id ( ’ foo ’ ) ;5 }6 r e t u r n cached f oo ;7 }
• Use $foo() instead of getElementById(’foo’).
Florian Loitsch JavaScript Optimization July 14, 2010 27 / 51
Event handlers
• Individual event handlers for many elements can be costly.
1 f u n c t i o n c r e a t eHand l e r ( i ) {2 r e t u r n f u n c t i o n ( ) {3 a l e r t (” c l i c k i n g ” + i ) ;4 r e t u r n f a l s e ;5 }6 }7
8 window . on load = f u n c t i o n ( ){9 v a r x = document . getElementsByTagName (” a ” ) ;
10 f o r ( v a r i = 0 ; i < x . l e n g t h ; i ++){11 x [ i ] . o n c l i c k = c r e a t eHand l e r ( i ) ;12 }13 }
Florian Loitsch JavaScript Optimization July 14, 2010 28 / 51
Event handlers
• Use event-delegation instead.
• Faster to set up, and uses less memory.
1 f u n c t i o n ge tEventTarge t ( e ) {2 e = e | | window . even t ;3 r e t u r n e . t a r g e t | | e . s r cE l ement ;4 }5
6 document . o n c l i c k = f u n c t i o n ( e ) {7 t = getEventTarge t ( e ) ;8 i f ( t . nodeName == ’A ’ ) {9 a l e r t (” c l i c k e d A” ) ;
10 }11 }
Florian Loitsch JavaScript Optimization July 14, 2010 29 / 51
Outline
1 IntroductionJavaScript
2 When not to Optimize
3 XHR and DOM
4 Don’ts
5 Miscellaneous
6 Final Words
Dynamism
• JavaScript is a dynamic language.
• Difficult to optimize (in JITs).
• Some constructs make optimizations even more difficult.• eval• with
Florian Loitsch JavaScript Optimization July 14, 2010 31 / 51
eval
eval is evil.
• There are reasons to use eval, but
• it’s easy to get wrong. Chances are the code is• either fragile,• a security problem,• overly complicated,• or all of the above.
• If done wrong slows down seemingly unrelated code.
Florian Loitsch JavaScript Optimization July 14, 2010 32 / 51
eval cont.
eval
1 v a r benchmark = ( f u n c t i o n ( ) {2 v a r p r i v a t e v a r i a b l e ;3 f u n c t i o n f oo ( ) {4 p r i v a t e v a r i a b l e = 0 ;5 f o r ( v a r i = 0 ;6 i < 100000;7 i++) {8 p r i v a t e v a r i a b l e += i ;9 }
10 i f ( e v a l (”42”) != 42)11 throw ”oops ” ;12 }13 r e t u r n f oo ;14 } ) ( ) ;
v a r benchmark2 = ( f u n c t i o n ( ) {v a r p r i v a t e v a r i a b l e ;f u n c t i o n f oo ( ) {
p r i v a t e v a r i a b l e = 0 ;f o r ( v a r i = 0 ;
i < 100000;i++) {
p r i v a t e v a r i a b l e += i ;}i f ( e v a l . c a l l ( n u l l , ”42”) != 42)
throw ”oops ” ;}r e t u r n f oo ;
} ) ( ) ;
• eval in left version could introduce shadowing local.
• Right version is 8 times faster.
Florian Loitsch JavaScript Optimization July 14, 2010 33 / 51
eval cont.
eval
1 v a r benchmark = ( f u n c t i o n ( ) {2 v a r p r i v a t e v a r i a b l e ;3 f u n c t i o n f oo ( ) {4 p r i v a t e v a r i a b l e = 0 ;5 f o r ( v a r i = 0 ;6 i < 100000;7 i++) {8 p r i v a t e v a r i a b l e += i ;9 }
10 i f ( e v a l (”42”) != 42)11 throw ”oops ” ;12 }13 r e t u r n f oo ;14 } ) ( ) ;
v a r benchmark2 = ( f u n c t i o n ( ) {v a r p r i v a t e v a r i a b l e ;f u n c t i o n f oo ( ) {
p r i v a t e v a r i a b l e = 0 ;f o r ( v a r i = 0 ;
i < 100000;i++) {
p r i v a t e v a r i a b l e += i ;}i f ( e v a l . c a l l ( n u l l , ”42”) != 42)
throw ”oops ” ;}r e t u r n f oo ;
} ) ( ) ;
• eval in left version could introduce shadowing local.
• Right version is 8 times faster.
Florian Loitsch JavaScript Optimization July 14, 2010 33 / 51
eval - Correct usage
Don’t use eval to read json-data. Use JSON.parse instead.If you really need to use eval consider one of the following alternatives.
f u n c t i o n e v a l 0 ( s t r ) {r e t u r n e v a l ( s t r ) ; // might clash with "str" variable.
}
f u n c t i o n e v a l 1 ( s t r ) {r e t u r n Funct ion ( s t r ) ( ) ; // requires "return ".
}
f u n c t i o n e v a l 2 ( s t r ) {r e t u r n e v a l . c a l l ( n u l l , s t r ) ; // spec -conform with ES5.
}}
Florian Loitsch JavaScript Optimization July 14, 2010 34 / 51
with
with introduces all properties of a given object as local variables.
f u n c t i o n f oo ( ) {v a r o = { x : 1 , y : 2 } ;with ( o ) {
a l e r t ( x + ” ” + y ) ; // => 1 2
}}
Florian Loitsch JavaScript Optimization July 14, 2010 35 / 51
with - Example
with requires check at every variable access.
f u n c t i o n f oo ( ) {v a r x = 10 ;v a r z = 30 ;v a r o = { x : 1 , y : 2 } ;with ( o ) {
a l e r t ( x + ” ” + y ) ; // => 1 2
a l e r t ( z ) ; // => 30
d e l e t e ( o . x ) ;a l e r t ( x + ” ” + y ) ; // => 10 2
o . z = 3 ;a l e r t ( z ) ; // => 3
}}
Florian Loitsch JavaScript Optimization July 14, 2010 36 / 51
with - Example 2
A more confusing example.
f u n c t i o n f oo ( ) {v a r f ;v a r x = 1 ;v a r o = {} ;with ( o ) {
f = f u n c t i o n ( ) { r e t u r n x ; } ;}a l e r t ( f ( ) ) ; // => 1
o . x = 42 ;a l e r t ( f ( ) ) ; // => 42
}
Florian Loitsch JavaScript Optimization July 14, 2010 37 / 51
with - Example 3
But surely we can optimize the obvious ones.
f u n c t i o n f oo ( ) {v a r z = 10 ;v a r o = { x : 2 , y : 3 } ;with ( o ) {
a l e r t ( x + ” ” + y + ” ” + z ) ; // => 2 3 10 ?
}}
Not if the prototype of Object has been modified.
Object . p r o t o t yp e . z = 4 ;
Florian Loitsch JavaScript Optimization July 14, 2010 38 / 51
with - Example 3
But surely we can optimize the obvious ones.
f u n c t i o n f oo ( ) {v a r z = 10 ;v a r o = { x : 2 , y : 3 } ;with ( o ) {
a l e r t ( x + ” ” + y + ” ” + z ) ; // => 2 3 10 ?
}}
Not if the prototype of Object has been modified.
Object . p r o t o t yp e . z = 4 ;
Florian Loitsch JavaScript Optimization July 14, 2010 38 / 51
with - Conclusion
Don’t use with.
Florian Loitsch JavaScript Optimization July 14, 2010 39 / 51
Outline
1 IntroductionJavaScript
2 When not to Optimize
3 XHR and DOM
4 Don’ts
5 Miscellaneous
6 Final Words
Optimistic Optimizations
JITs optimize optimistically. Penalties when wrong.
• Branch Prediction.
• Typing.
Florian Loitsch JavaScript Optimization July 14, 2010 41 / 51
Tracing
Firefox uses tracing to optimize code.
1 Detect hot spots.
2 Record path at the hot spot.
3 Optimize recorded path.
4 Use optimized code in next iteration.
Slow, when assumptions don’t hold.
Florian Loitsch JavaScript Optimization July 14, 2010 42 / 51
Typing
V8 optimistically assigns types to objects and variables.
f u n c t i o n A( x , y ) {t h i s . x = x ;t h i s . y = y ;
}v a r a = new A(1 , 2 ) ;
Very likely that many objects have same type.
f u n c t i o n f oo ( o b j e c t s ) {v a r r e s u l t = 0 ;f o r ( v a r i = 0 ; i < o b j e c t s . l e n g t h ; ++i ) {
r e s u l t += ob j e c t s [ i ] . x + o b j e c t s [ i ] . y ;}
}
Florian Loitsch JavaScript Optimization July 14, 2010 43 / 51
Typing
V8 optimistically assigns types to objects and variables.
f u n c t i o n A( x , y ) {t h i s . x = x ;t h i s . y = y ;
}v a r a = new A(1 , 2 ) ;
Very likely that many objects have same type.
f u n c t i o n f oo ( o b j e c t s ) {v a r r e s u l t = 0 ;f o r ( v a r i = 0 ; i < o b j e c t s . l e n g t h ; ++i ) {
r e s u l t += ob j e c t s [ i ] . x + o b j e c t s [ i ] . y ;}
}
Florian Loitsch JavaScript Optimization July 14, 2010 43 / 51
Hidden Classes
f u n c t i o n Po in t ( x , y ) {t h i s . x = x ;t h i s . y = y ;
}
v a r p = new Po in t (1 , 2 ) ;v a r q = new Po in t (3 , 4 ) ; C0
p
Florian Loitsch JavaScript Optimization July 14, 2010 44 / 51
Hidden Classes
f u n c t i o n Po in t ( x , y ) {t h i s . x = x ;t h i s . y = y ;
}
v a r p = new Po in t (1 , 2 ) ;v a r q = new Po in t (3 , 4 ) ; C0 C1
x 0
p 1
Florian Loitsch JavaScript Optimization July 14, 2010 44 / 51
Hidden Classes
f u n c t i o n Po in t ( x , y ) {t h i s . x = x ;t h i s . y = y ;
}
v a r p = new Po in t (1 , 2 ) ;v a r q = new Po in t (3 , 4 ) ; C0 C1 C2
x 0 x 0y 1
p 1 2
Florian Loitsch JavaScript Optimization July 14, 2010 44 / 51
Hidden Classes
f u n c t i o n Po in t ( x , y ) {t h i s . x = x ;t h i s . y = y ;
}
v a r p = new Po in t (1 , 2 ) ;v a r q = new Po in t (3 , 4 ) ; C0 C1 C2
x 0 x 0y 1
p
q
1 2
3 4
Florian Loitsch JavaScript Optimization July 14, 2010 44 / 51
Typing cont.
Don’t make your program look dynamic if it is static.
1 f u n c t i o n f oo ( a ) {2 v a r r e s u l t = 0 ;3 f o r ( v a r i = 0 ;4 i < 100000;5 ++i ) {6 r e s u l t += a . x + a . y ;7 }8 r e t u r n r e s u l t ;9 }
10
11 f u n c t i o n A( x , y , z ) {12 t h i s . x = x ;13 t h i s . y = y ;14 t h i s . z = z ;15 }16
17 v a r a = new A(1 , 2 , 3 ) ;18 d e l e t e a . z ;19 f oo ( a ) ;
f u n c t i o n f oo ( a ) {v a r r e s u l t = 0 ;f o r ( v a r i = 0 ;
i < 100000;++i ) {
r e s u l t += a . x + a . y ;}r e t u r n r e s u l t ;
}
f u n c t i o n A( x , y , z ) {t h i s . x = x ;t h i s . y = y ;t h i s . z = z ;
}
v a r a = new A(1 , 2 , 3 ) ;a . z = unde f i n ed ;foo ( a ) ;
Right version is three times faster.
Florian Loitsch JavaScript Optimization July 14, 2010 45 / 51
Typing cont.
Don’t make your program look dynamic if it is static.
1 f u n c t i o n f oo ( a ) {2 v a r r e s u l t = 0 ;3 f o r ( v a r i = 0 ;4 i < 100000;5 ++i ) {6 r e s u l t += a . x + a . y ;7 }8 r e t u r n r e s u l t ;9 }
10
11 f u n c t i o n A( x , y , z ) {12 t h i s . x = x ;13 t h i s . y = y ;14 t h i s . z = z ;15 }16
17 v a r a = new A(1 , 2 , 3 ) ;18 d e l e t e a . z ;19 f oo ( a ) ;
f u n c t i o n f oo ( a ) {v a r r e s u l t = 0 ;f o r ( v a r i = 0 ;
i < 100000;++i ) {
r e s u l t += a . x + a . y ;}r e t u r n r e s u l t ;
}
f u n c t i o n A( x , y , z ) {t h i s . x = x ;t h i s . y = y ;t h i s . z = z ;
}
v a r a = new A(1 , 2 , 3 ) ;a . z = unde f i n ed ;foo ( a ) ;
Right version is three times faster.
Florian Loitsch JavaScript Optimization July 14, 2010 45 / 51
Confusion
Don’t confuse the JIT.
• Avoid changing global variables through the this object.
• Don’t use arrays as hashtables/objects.
• Don’t use objects as arrays.
• Only use identifiers in objects. o[’#9(>’] will remove hidden classand turn your object into a slow hashtable.
Florian Loitsch JavaScript Optimization July 14, 2010 46 / 51
Antipatterns
Common Antipatterns:
• Deleting properties to win memory. (Deletion in hashtables is fine.)
• Using for-in on an array instead of iterating from 0 to length.
• array.indexOf(foo) == 0 vs. array[0] == foo. Same forstrings.
• Starting regular expressions with .*.
Florian Loitsch JavaScript Optimization July 14, 2010 47 / 51
Outline
1 IntroductionJavaScript
2 When not to Optimize
3 XHR and DOM
4 Don’ts
5 Miscellaneous
6 Final Words
Benchmarks
• VM developers live by benchmarks.
• If you have good benchmarks, send them to us.
• Good benchmarks are difficult. Beware of optimizations (tracing,etc.).
Florian Loitsch JavaScript Optimization July 14, 2010 49 / 51
Advice
Don’t optimize if you don’t need to.
• JITs change.
• Readability first.
Let others speed up your work:
• (Make people) switch to new browsers (or Chrome Frame).
• Use GWT.
Don’t assume.
• JITs are complex.
• Profile your code before optimizing.
• Learn details of JavaScript.
Florian Loitsch JavaScript Optimization July 14, 2010 50 / 51
Questions
Time for Q’n’A!
Florian Loitsch JavaScript Optimization July 14, 2010 51 / 51