-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature suggestion: optimizing var
definitions
#75
Comments
Also, if you could determine that Have to be careful of inner closures, probably pretty unsafe, could be fun though! 😛 |
The optimisation above would be completely safe though |
The optimization is only safe when the vars contain primitives, isn't it? |
@staabm I think it's safe when the expression-node being assigned to the variables has |
In general it is safe to optimise: var x = E, y = E; to var x = E, y = x; For all E provided that E:
e.g. The following would not be safe to optimise even though var x = Math.random(), y = Math.random(); The following would be though: function get() { return 5; }
var x = get(), y = get(); The follwoing would not because function get() { return {}; }
var x = get(), y = get(); but the following would be: var val = {};
var x = val, y = val; |
@ForbesLindesay You're definitely right, sorry for being inaccurate. |
Interestingly it would be safe with certain very specific side effects, but this is probably more in depth analysis than we'd want to do: var got = false;
function get() {
return got = true;
}
var x = get(), y = get(); because although |
No worries @abody these things are tricky at the best of times, my initial intuition which I started writing down was completely false. |
@ForbesLindesay Yeah that's too deep an analysis -- you'd expect the person who's coding the above would optimize it himself/herself. I initially just thought about this optimization because I had initialized lots of variables to |
@ForbesLindesay There are also cases where defined getters might have side effects, but it's too hard to spot those. That's why I suggested to just make the optimization disabled by default/unsafe. |
You wouldn't want it to run for anything that could be a getter, because that's way too likely to be unsafe. Just code the optimisation so it only does it when we know it's safe (i.e. expressions that don't contain property accessors, function calls, object literals, array literals or regexp literals). Perhaps more significantly, it slows runtime performance if not done carefully: http://jsperf.com/optimizing-var-definitions (needs more runs to be statistically significant). I suspect it will have negligible or no affect on file size once gzip has done it's thing. It may ultimately not be worth optimising, unless you want to handle the case of a call to a function which has no side affects and always returns the same value. e.g. the performance gain of optimising the following would be huge: function factorial(n) {
if (n === 0) return 1;
else if (n > 0) return n * factorial(n - 1);
}
var a = factorial(50), b = factorial(50); How often such code actually appears though I'm not sure. Do we know whether UglifyJS already tracks which functions are 'pure' functions (i.e. no side effects and always return the exact same value for the same input)? If it does, this may be doable, if not then it's a fair bit of work for minimal gain. |
@ForbesLindesay Oh interesting benchmark; I'm running Chrome 23 and actually the separate declaration is slower than either the remaining two. You can never really know if there's a getter defined; and it needn't be a property accessor to be unsafe (think about the case where you do Perhaps you're right about the negligible difference in file size. I'm not sure whether UglifyJS keeps track of such 'pure' functions, perhaps @mishoo can clarify? |
OK, anything that can be a getter then, which should include all property accessors and any variable that's not found in the current scope (and therefore could be a property on the window) |
this.defineGetter wouldn't make any difference, I've already said we should exempt property accessors and 'global' variables, |
It might need to also be disabled inside |
@ForbesLindesay Oh. Never mind about my previous comment. I completely missed the point. Sorry! |
For anyone else reading: function test() {
this.__defineGetter__("x", function() {
alert("This is evil.");
return 5;
});
return x;
}
console.log(new test()); just throws a reference error and if you're in stict mode then so does the above code. The above code in non-strict mode is just assigning x as a property of the global object so the following is also fine: (function () {
var x = 10;
function test() {
this.__defineGetter__("x", function() {
alert("This is evil.");
return 5;
});
return x;
}
console.log(test()); //logs 10
}()) |
This might seem simple enough to wonder “is there a good reason not to do it?”, but it's in fact quite complicated in the general case. Doing it only for the case This comment also applies to #68 and #27 — data flow analysis would help dealing with them too. |
@mishoo I see. Well, I just tested with the Google Closure Compiler (it actually took me some time to work around it optimizing away the variables and replacing them with their values). It appears it doesn't do such an optimization, either. Thanks for your explanation! |
This could be easily doable by using register-based bytecode, instead of AST, as IR. You'd lose comments but you could always add a bytecode for comments if you wanted to :P |
@SoniEx2 Unless you're very careful with your design, transformations on that bytecode would result in behaviour that cannot be translated back to JavaScript. |
@michaelficarra You can turn Lua bytecode back and forth, at least... altho slightly inefficiently for some things... Maybe optimize based on bytecode and minify based on JS? (JS -> bytecode -> optimize -> JS -> minify) |
Sure, it can be done, but that optimisation step is going to be tricky. You have to make sure the bytecode's overall structure remains representable in JavaScript, or at least can be filled in to be representable. As an example, a bytecode with arbitrary jumps can jump into the middle of a "loop", but that behaviour cannot be represented in JavaScript; at least, not easily. |
I see what you mean. But if you want to optimize efficiently an IR is usually the way to go. |
this isnt fixed yet? |
Hi!
I came across a case where there's a possible trick that can reduce size significantly. Take the following piece of code:
It compresses to:
Can't it be optimized to the following:
Or is there something I'm missing? I think it should be available as an unsafe optimization.
Thanks!
The text was updated successfully, but these errors were encountered: