Tuesday, October 20th, 2009

Microsoft Ajax Minifier VS YUI Compressor

Category: JavaScript, Microsoft, YUI

<p>I have discovered only yesterday the Announcing Microsoft Ajax Library (Preview 6) and the Microsoft Ajax Minifier post and using Visual Studio on daily basis I could not miss an instant minifier test.

First of all, my apologizes for the wrong tweet and the comment left in the related post. I have spotted a false positive during one of my tests where object properties where accessed dynamically via string concatenation. The result I have instantly noticed was something like:

javascript
< view plain text >
  1. o[a+b]=c;

Where a and b were two different strings already defined elsewhere. Above practice could probably save few bytes but performances will be slower due to run-time string creation to discover the property/method rather than direct access as obj.prop is.

Nothing new so far, just the reason I reacted that fast, so let’s try to forget my error starting to compare these two minifier.

Different Defaults

Main YUI Compressor settings are:

  1. –nomunge, Minify only, do not obfuscate
  2. –preserve-semi,  Preserve all semicolons
  3. –disable-optimizations, Disable all micro optimizations

These jar arguments need to be explicit and are able to make YUIC minification less greedy.
As opposite behavior, Ajax Minifier requires explicit optimizations such hypercrunch, the YUI munge synonym, and an extra C to combine frequent strings into variables.

javascript
< view plain text >
  1. // Ajax Minifier Best Option
  2. ajaxmin.exe -hc input.js -o output.js
  3.  
  4. // YUI Compressor Best Option
  5. java -jar yuicompressor-2.4.2.jar input.js -o output.js

Ajax Minifier Hypercrunch Plus C

One reason Ajax Minifier could claim better ratio is the C option. While the hypercrunch option produces almost the same YUIC munged output, the extra parameter will try to understand how many times a literal string is repeated inside a block and, if more than 2 times, it will assign this string to a variable.

javascript
< view plain text >
  1. (function(){
  2. node["onclick"] = function onclick(){
  3. node["onclick"] = null;
  4. setTimeout(function(){
  5. node["onclick"] = onclick;
  6. }, 1000);
  7. };
  8. })();

Above piece of code will produce these outputs:

javascript
< view plain text >
  1. // Ajax Minifier
  2. (function(){var a="onclick";node[a]=function b(){node[a]=null;setTimeout(function(){node[a]=b},1e3)}})()
  3.  
  4. // YUI Compressor
  5. (function(){node.onclick=function a(){node.onclick=null;setTimeout(function(){node.onclick=a},1000)}})();

Above example is a classic one where the YUI Compressor better understands the logic, removing repeated and unnecessary dynamic access, without performing literal checks or number translation. Has anybody spotted a 1e3 rather than 1000?

Stripped Comments

Using a different block of code, we can spot new things to care about:

javascript
< view plain text >
  1. var o = (function(){
  2. var o = {};
  3. /*!
  4. important comment
  5. */
  6. var i = 0;
  7. o["on" + "click"] = function(){};
  8. o["on" + "mouse" + "over"] =
  9. o["on" + "mouse" + "out"] =
  10. o["on" + "mouse" + "move"] = o["on" + "click"];
  11. if(i)
  12. i++
  13. ;
  14. return o
  15. })();

First of all we have a well known comment syntax which aim is to preserve comments such Licenses, Copy Rights, or any comment we would leave there.
This is indeed the output via YUI Compressor:

javascript
< view plain text >
  1. var o=(function(){var b={};
  2. /*
  3. important comment
  4. */
  5. var a=0;b.onclick=function(){};b.onmouseover=b.onmouseout=b.onmousemove=b.onclick;if(a){a++}return b})();

Other things is the same kind of optimization YUIC performed over static literal concatenation. In few words YUIC optimized that property or method access improving indirectly performances. The only “waste of bytes” are two brackets rather than original if(a)a++; snippet, something Ajax Minifier does not change at all.

javascript
< view plain text >
  1. var o=function(){var c="mouse",b="on",a={},d=0;a[b+"click"]=function(){};a[b+c+"over"]=a[b+c+"out"]=a[b+c+"move"]=a[b+"click"];if(d)d++;return a}()

Even if we minify the source via -hl rather than -hc, Ajax Minifier won’t optimize dynamic access. This let us think that this minifier is not able to speedup or adjust our or third parts libraries code.
At the same time we can spot that Ajax Minifier strips out comments without respecting the /*! combination. To be honest, I could never expect something like this, specially for a library and a minifier “perfectly combined” with jQuery library: is every .NET developer stripping out jQuery credits? Hopefully no!

Ajax Minifier, Loads Of Options

  1. S, to avoid any kind of output during compression and put eventually errors in the stderr
  2. A, to analyze the code
  3. Z, to add semicolon where necessary, rather than new lines
  4. L, to avoid combined literals
  5. Eo or Ei, to decide ASCII or input dependent encoding scheme
  6. W, to set warning level
  7. P, to tidy up a compressed output making it pretty (the Visual Studio meaning of pretty)
  8. R, to combine literals in more than a file, as example jQuery sources into jQuery for custom builds
  9. I, to echo in console the original source
  10. D, to remove debugger statements
  11. 3, to do not add IE specific code, respecting strict standards (???)

Many others I could not test properly or I am not sure about, as is for the /J option, which let me write eval(“alert(123)”) without problems and without a single warning.

Code Analyzer

This is probably one of the most interesting Ajax Minifier options, something implicit via Rhino in YUI Compressor but not directly exposed. The code analyzer could give us some info without necessarily compress code.

javascript
< view plain text >
  1. var o = (function(){
  2.     var o = function(){};
  3.     var i = 0;
  4.     if(i)
  5.         i++
  6.     ;
  7.     return o
  8. })();

If we pass this code via Ajax Minifier and /A option, we’ll obtain this result:

Crunching file 'test.js'...
test.js(7,5-10): improper technique warning JS1267: Always use full statement bl
ocks: if(i)

Global Objects
  o [global var]

Function Expression anonymous_0 - starts at line 1, col 9 [0 references]
  i [local var]
  o [local function]

Function Expression "o" - starts at line 2, col 12

var o=function(){var o=function(){},i=0;if(i)i++;return o}()
Done.

Interesting how Ajax Minifier knows that brackets are a good practice, and this time there is a warning.
Moreover, it is possible to understand how this application evaluates JScript behavior defining function expression everything, anonymous and/or assignment included.
We can also have a quick summary about all used variables in a script, local or global, understanding scopes and studying possible conflicts.

Conditional Comments Nonsense

Last but not least, the Ajax Minifier strips out by default JScript conditional comments. The result is that code will be preserved but in a totally obtrusive way if the browser is not Internet Explorer. Be careful!

javascript
< view plain text >
  1. var b = false;
  2. /*@cc_on
  3. b = true;
  4. @*/
  5.  
  6. // will be
  7. var b=false;b=true

No Winner Yet

I like bits and bops from both proposals, the YUI Compressor, stable, and widely adopted, but unable to perform some Ajax Minifier optimization, and the latter one, not necessary the best choice as we have seen before, but a truly good alternative, specially for Visual Studio based developers.
I found clever the Ajax Minifier concatenated variables declaration, normal in YUIC but not performed if there is a comment in between (var a=1;/**/var b=2;) while I consider a better tool, performances speaking, the YUI Compressor, since ignored Ajax Minifier dynamic access and literals to variables, when specified, could slightly increase run-time and execution speed compared to the same output produced via YUI Compressor. Not official tests yet, but I am planning to!

Posted by webreflection at 10:54 am
16 Comments

++---
2.8 rating from 49 votes

16 Comments »

Comments feed TrackBack URI

The conditionnal comment semi-removal, is a very obvious attempt to break compatibility with other browsers.

I can understand that microsoft tries to push his software, but delibirate attempt to break his users’ software is unaceptable.

The string accessor optimisation is a good idea in theory, but anyone coming from a javascript/actionscript (or any interpreted language) knows that string concatenation and objects lookup are a performance issue.
I’m not even sure this is worth it, considering than the LZ77 algorithm in Gunzip should reduce theses string.

Numbers and brackets optimisation are pretty cool.

Comment by ywg — October 20, 2009

@ywg
If you need another approach, you might want to check out Kariem’s nAnt Build task for miifying and gunzipping as part of an nAnt build script …
http://ra-ajax.org/custom-nant-jsmin-task-for-minifying-javascript-files.blog
.
and
http://ra-ajax.org/webresource-axd-and-javascript-gzip-compression-a-simple-solution.blog
.
We’re using it in Ra ourselves …

Comment by ThomasHansen — October 20, 2009

great information in the article. i am not sure why it has not been edited for basic grammatical errors to increase readability.

Comment by ajpiano — October 20, 2009

I tested compressing a 944KB javascript source file using both YUIC and the new MS AJAX compression tool.

YUIC output = 565KB
MS AJAX Minifier output = 533KB

so, MS wins here, but when I ZIP’d the files up it turns out like this:

YUIC output zipped = 112KB
MS AJAX Minifier output zipped = 114KB

I’m still going to use YUIC considering the GZIP’d results for YUIC are smaller after being zipped.

Comment by leptons — October 20, 2009

@ywg — Really, there is no evil conspiracy here on the part of Microsoft :) We’ve been using the Microsoft Ajax Minifier for several years to minify the JavaScript files in the Microsoft Ajax Library and the AJAX Control Toolkit. Because we found the tool to be so useful internally, we wanted to share it with the community.

There are a number of interesting suggestions in the article. For example, we do have to look at supporting conditional-compilation comments and the of /*! … */ comment notation for a future release.

One thing that the article above did not mention is that the Microsoft Ajax Minifier includes an MSBuild task that you can use to minify your JavaScript files when using Visual Studio automatically. If you want to read a more in-depth description of the Microsoft Ajax Minifier, see my blog post at:

http://stephenwalther.com/blog/archive/2009/10/16/using-the-new-microsoft-ajax-minifier.aspx

Thanks for the article.

— Stephen Walther (Microsoft Ajax Team)

Comment by swalther — October 20, 2009

String concatenation to build property names?! Nice try – but incurring a performance penalty in return for a minimaly faster load doesn’t do it for me. That feature should be killed of ASAP.

I’ll stick with YUI – at least it doesn’t mess with my code.

Comment by rasmusfl0e — October 20, 2009

The multitude of options don’t exactly inspire confidence, but I’m sure MS will reduce them in future versions. Who needs IE specific compression these days?

The MSBuild integration is a rather nice feature, but the .NET port of
YUI Compressor
also does that very well. What’s more it allows to combine files in addition to compressing them.

Comment by anoncoward — October 21, 2009

@Stephen Walther

Sorry to say things like that, I don’t wan’t to degrade the quality of your (and Microsoft Dev Team) work…
But on this particular point I had a really hard time believing this is just an “accidental regression” and not a corporate decision. Either you don’t support them at all and strip them out, or you just leave them as they are.
But not understanding that “conditionnal” stands for “print on certain conditions”… no I don’t buy this.

Comment by ywg — October 21, 2009

Something I don’t understand:

var a = “abc”;
b[a] = 123; // less efficient?
b.abc = 123; // more efficient?

Seems like accessing the property with [] would be roughly the same as accessing it with a . operator.

Comment by shadedecho — October 21, 2009

@shadedecho from parsing point of view you need to retrieve the variable, its value eventually “casted” as string, and only after access the property name if any. These are 3 operations rather than just 1: access the property name if any. In this case we can spot static strings concatenations possibly replaced by variables, then concatenated, and only after the property name will be known. YUI Compressor does something clever and it is almost the same SpiderMonkey engine does if you create a Function(“o”, ‘return o["key"]‘) it will be created as return o.key which is more direct and logical.

Comment by webreflection — October 21, 2009

@webreflection — why does it need to cast as a string if it’s already a string?

I understand that [a] is two operations (in general) versus one operation for .abc. But doesn’t that then mean that ["abc"] should just be the same as .abc?

And if that’s true, it would seem like engines like TraceMonkey (and perhaps others) should be able to tell if the variable being used for the property lookup is, in effect, a “constant”, meaning it is used as an l-value only once, in its initial declaration/assignment, and isn’t changed throughout the code. If they could tell that, couldn’t they pre-optimize code execution for the use of “constants” for property lookup, and skip all the variable lookup and casting junk, making [a] equivalent roughly to .abc?

I ask this because I find it quite a useful technique to turn repeated property references into [a] style with constants, for the purposes of compression. It’s troublesome that this trades file size reduction for a performance hit. With minification so prevalent, I would think it would be becoming a common enough thing that the JS engines should be looking to optimize for it.

Comment by shadedecho — October 22, 2009

@shadedecho I am not talking about o["a"] VS o.a, which I suppose is almost the equivalent for every JS engine and/or optimized run-time, I am talking about o[a] VS o.a
If there is a variable there is no guarantee that variable is the expected string. A variable needs to be “discovered” in the current scope and it should be a primitive “string” or an instanceof String, or whatever Object with a valueOf or a toString call, do you follow me?
A simple example is an Array loop, where arr[i] will point to a different value and “i” is a primitive “number” (int)
Even in a small block, with or without “let” scope, rather than “var”, “i” needs to be discovered and its value needs to be assigned for that moment.
This is the reason extreme optimizations over loops use direct access if the length is reasonable (arr[0], arr[1], arr[2], arr[3]) avoiding both dynamic access and integer incrementation (++i)
Moreover, if we have o["a"] more than twice, let’s say 3 times, the variable declaration plus square brackets will cost more bytes rather than less (var a=”a”;o[a];o[a];o[a]; VS o.a;o.a;o.a;) and being unnecessary, plus forcing the dynamic access, do we agree YUI Compressor provides a better solution here? That is my point about performances and dynamic access, fine sometimes, but not necessary better :)

Comment by webreflection — October 22, 2009

The Ajax Minifier does not convert property access into dynamic access — that doesn’t seem very clear here. If you have foo.bar=baz, it does _not_ convert it to foo[a]=baz. What it does do is alias literals, so if foo["bar"] = baz is converted to foo[a]=baz it’s only because “bar” was a literal that was used enough times for that to save space, it has nothing to do with changing how property access occurs. That does incur a slight performance penelty vs. foo["bar"]. Whether that is enough to matter for your scenario is up to you — it’s not even the default option.

Comment by InfinitiesLoop — October 22, 2009

@InfinitiesLoop it makes access dynamic due to variable assignment while YUI Compressor understands unnecessary literals and it strips out unnecessary chars as well. In both case I hope we agree that there is a property access change, and that is what I wrote.
We should remember that most libraries are inside a big closure loads of nested scopes. A dynamic access to the top closure variable to discover a static literal is, in my opinion, not worth it, performances speaking, but it could save a couple of bytes.
Thanks in any case for explanation, I guess if you had to provide one, this point was not perfectly clear. Regards

Comment by webreflection — October 22, 2009

Yup — when not using the HC option, it would definitely be an improvement to convert foo["bar"] to foo.bar. When using HC, well, use with care, and understand the implications. And always check the gzipped result since that is what matters most — it would be a nice feature, actually, if the tool could do that for you.

Comment by InfinitiesLoop — October 22, 2009

Aha, seems like spammers finally figured out how to circumvent Ajaxian’s extremely advanced CAPTCHA ;)

Comment by BertrandLeRoy — January 20, 2010

Leave a comment

You must be logged in to post a comment.