Runtime Code Generation:
This is one of my favorite features if I know that I am going to run a piece of code a huge number of times but only need to read the parameters once. This one time parameter (or constant parameters) lets me spend some time compiling code. NOTE: you should not abuse code generation and do it all the time.
How to use it:
Function / eval are the only ways to do this in Javascript. They do not generate sub-optimal performance functions (see: http://jsperf.com/function-vs-constructor-vs-eval). The functions generate code that is as fast as anything else. I would highly recommend using Function instead of eval. Using Function does not allow access to variables outside of your function except parameters and the "this" variable.
Example:
A simple cache function for a CSS selector like language (only takes tags and ids, is buggy etc.)
function SelectorFunction(selector) {var src = String(selector).match(/\#?[^a-zA-Z_\-$0-9]/g)src.map(function(selector_item){return selector_item.charAt(0) === "."? "child = document.getElementById(\"" +selector_item.slice(1)+ "\"); if(child.compareDocumentPosition(node) !== Node.DOCUMENT_POSITION_CONTAINED_BY) return undefined; node = child": "node.getElementsByTagName(\"" +selector_item+ "\")"})return Function("node","var child = null;" + src.join(";"))}
When to use it:
- When you have time to spare (setup / idle time / etc.)
- You know you are going to be able to infer something from your parameters that lets you hard code or pre-compute values
Pros:
- If done right, your code will gain quite a bit of speed and help remove possible bottlenecks
- Caching precomputed values means parsing them again wont occur and wont use up memory in the process
Cons:
- If abused, this will slow down your code
- Cached values may not be wanted for some things (dynamic settings)
Language Oddities
As a language Javascript has some odd performance characteristics.
This / Closures:
- Some engines, v8 in particular, aggressively optimize the lookup operator. You cannot do that optimization on a this variable since it is an unknown at compile time for some other inferences.
- Closures are faster than the this variable lookup time in v8.
- Closures that completely hide another closure will skip the closure above it in a scope chain (except function arguments and the catch variable in v8, this cannot be done to escape a with statement)
Numbers:
- Javascript is terrible at bitwise operators, TERRIBLE. Especially if you start mixing them with numbers that are acting like floating point numbers (this is the default number system in Javascript, engines optimize when you swap into integral calculation to prevent multiple casts over and over again).
Operators:
- The in operator is slower than the lookup operator. Why? It checks the entire prototype chain. The lookup operator in most new engines only performs a single check.
- The if statement is slower than the ternary operator which is slower than the or operator for single expression results (http://jsperf.com/default-args-or-vs-if/2)
- Avoid the delete operator unless you have an extremely long lived object. V8 will have to do some hoodoo in the background if you use it to reconcile some lookup optimizations which will take time.
Statements:
- The with statement is slow... I mean really slow. Slower than emulating it with the dot operator (which means something slightly different, but oh well). This is because it must check the entire prototype chain of the object (akin to the in operator), and because almost no engine optimizes this statement due to its lack of use.
- Switch statements are fast, but unless you are only using Strings if/else branches can be optimized better by most engines.
Lies
A lot of what I have read about Javascript meta-programming is a lie.
- If statements will not be precomputed and cached. They however are often
- Objects as hashes only work until a certain size limit. Then they start to slow down due to GC (13~ million properties in the current v8, but then again who gets that far?).
- StringVariable + "". This is the devils work for performance. It creates a full copy due to .toString etc this cannot be optimized out.
Memory Consumption
A lot of people do things that seem innovative, but tend towards high memory consumption.
- Buffering! Do not do this when you only are going to pump out the data at the end! (In some cases you do not know if you want to pump it out if there is an error).
- Throwaway functions do take memory, don't abuse them, cache them if you can (well cache everything you can really).
- Please don't save extra properties onto host objects. Host objects tend to have slightly different GC rules (in IE 6-8 you can leak when messing with dom and not cleaning up!).
- Avoid globals! Globals will never be GC'ed just avoid them when you can (certainly if you are leaving an object that can grow and grow and then be unused).
0 comments:
Post a Comment