8 ms·
JavaScript Minification Benchmarks
- deanclatworthy 6y agoIf you’ve not yet used esbuild for a simple react or typescript project give it a try. It’s far far simpler than webpack and ridiculously quicker.
- capableweb 6y agoSeems almost Google Closure (advanced) fails in half of the tests but otherwise wins the tests if it's successful. Seems to early to claim any "winner" here before someone manages to fix the tests. A comment reads "google-closure-compiler is no longer maintained" but that's completely misleading. Maybe that's why the author didn't bother to fix their config for Google Closure?
- evmar 6y agoI was confused by this comment as well. I think what it's saying is that google-closure-compiler-js (note -js) is no longer maintained, and that to explain why it's using the (still maintained) google-closure-compiler (note no -js).
- randomfool 6y agoGoogle Closure's advanced optimizations require annotations throughout your codebase- in many codebases it may require significant effort to get this working. When you need that extra optimization it's great but can be a lot of effort otherwise. I'm most excited about esbuild though and I'm curious how it'll fit into a larger codebase & ecosystem. The author is aggressive about keeping it simple (for good reason!!)- the focus on speed is awesome.
- lenz2020 6y agoActually no. You rarely need to annotate anything. They type checking is independent from the optimization of you turn off the type based optimizations. You still get good results. This doesn't get you away from the requirements for advanced optimizations (property use restrictions in particular).
- capableweb 6y ago> it may require significant effort to get this working You're right, the author probably didn't provide externs and/or exports, so the tests fails. It's not really that much of an effort, especially if you're supposed to be testing the tool itself. Considering that the author is already testing libraries that have externs/exports written for them already, just shows the lack of care of the tests.
- newlisper 6y agoGoogle Closure is the best at minification but it's also the worst less pragmatic option for 99% of projects.
- yladiz 6y agoOff topic for this, but I've always wondered, why do these tools call this process "minification", not "minimization"?
- austincheney 6y agoBecause it comes from the word minify, which I believe was created by Douglas Crockford. To my knowledge he created the first minify tool, JSMin, in 2001. He described using that tool as to minify.
- lhorie 6y agoBecause "minimize code" already has another meaning
- capableweb 6y agoThat's the first time I hear that. I always understood minification and minimization as synonyms. What would minimizing code mean for you?
- lmkg 6y agoMy parse of "minimizing code" is that it's an approach to writing code, with the goal of writing less code. I would expect it to include things like terse idioms, DRY, preferring data over code, preferring configuration over code, preferring libraries over rolling your own, managing scope, etc. All things about writing different code, which solves the problem but is not always semantically equivalent. By contrast, minification is mechanical semantic-preserving syntax transformations done to a completed program after it written.
- capableweb 6y agoI hear you. I always applied the term "minimalism/minimal code" for that style of programming, which I'm a follower of myself (except the lib/diy route, which tends to be more minimal if you diy).
- bumbledraven 6y agoWow, esbuild comes away as the clear winner overall. It’s always the fastest — often at least 10x as fast as many of the others – and its compression ratio was within 2% or so of the best in all cases.
- capableweb 6y ago> clear winner overall. It’s always the fastest If you're mostly worried about how quickly you can minify when you do minify, then you're worrying about the wrong thing. You want as small results as possible that can be executed the fastest, how long time it takes doesn't really matter as you only minify on delivery to end-users, not every time you make a change locally. So instead it seems Google Closure is the best, in the cases the author got it to work. Otherwise it's UglifyJS/Terser, depending on your needs.
- mschuetz 6y agoNo, I vastly prefer 10x faster build times and merely 2% file sizes over anything that's on order of magnitude slower. It's not even a competition.
- cj 6y agoOf course developers prefer faster build times. But what do the end-users who you write code for prefer? (Especially in a typical CI/CD environment where developers often don't need to monitor and wait for builds to finish) 2% sounds small. And it is, if your traffic is small. It's not small when you have millions of users.
- mschuetz 6y agoIt's tiny, and usually negligible in context with all the other data that needs to be transferred, even with millions of users. Millions of users might be the situation where I'd start thinking of integrating slower builds as an alternative, provided they can seamlessly live side-by-side with the fast build tools.
- 6y ago
- franciscop 6y agoI found esbuild a few months ago and... never gonna use a different one when doing things manually, the speed different is abyssal. It takes two orders of magnitude lower than the alternatives as shown in these benchmarks. On the other hand, with Node.js supporting native import/export, I also find myself bundling+minifying the code less and less. Just wish Create-React-App started using it, since I cannot be bothered to dig there enough to manually set it up (the goal of me using CRA is to avoid manual config).
- BiteCode_dev 6y agoNot exactly apples to apples, but I'd like to plug in Vite: https://github.com/vitejs/vite https://github.com/vitejs/vite Not that it makes fast minification (which I don't care that much about, I minify only on deploy, and it's a small % of the time it takes), but it does allow for a very fast workflow in dev mode: auto reload seems almost instant even with heavy transpiling. I only mention it because I assume people look at this articles and think about their dev process speed as well. If you haven't, give it a try: it's from the Vue author, so it's really clean, but works with react as well.
- ianertson 6y agoI also made some benchmarks, including my own bundler: https://www.github.com/sebbekarlsson/fjb/tree/master/benchmarks.md https://www.github.com/sebbekarlsson/fjb/tree/master/benchma...
- evmar 6y agoThis whole page is weirdly similar to a project I did, for the same purpose: https://evmar.github.io/js-min-bench/ https://evmar.github.io/js-min-bench/ What I discovered there is that the best minification is one where you just produce an empty file -- it's 0 bytes of output and extremely fast. And if you say "wait, I have have the additional requirement that you didn't break the code in the original file, obviously!", you then must define what exactly "break" means in that requirement-- for example, it could be that some user requires that someFunction().toString().charAt(10) == 'a', in which case even removing whitespace counts as breakage. You might think the above is just being overly pedantic or pathological, but it demonstrates a principle that actually ends up mattering, which is that you can't evaluate a minifier unless you define what a successful minification even means. Particularly in the case of more advanced transformation. I went into it more in this post: http://neugierig.org/software/blog/2019/04/js-minifiers.html http://neugierig.org/software/blog/2019/04/js-minifiers.html
- jiofih 6y agoSorry but what do you mean by “weirdly similar”? It’s a benchmark of minifiers against popular libraries, what’s the creative part?
- yoz-y 6y agoI guess a reasonable requirement for a momifier would be that the test suite passes 100% of tests after minification.
- emptysea 6y agoI think your example should instead be: someFunction.toString().charAt(10) == 'a' so you're getting the function's string representation, not its return value Also I've never even considered that minifiers can break your code. To be safe it seems you either need to run minification during test & dev or setup some linting to ban features that could break during minification.
- evmar 6y agoYou are totally right, thanks for catching my mistake. My blog post linked above goes more into the consequences -- ideally you'd formally define what the allowable language subset is.
- jacobp100 6y agoI just tried esbuild on a project that was using webpack. Had one bundle go up from 107kb to 111kb, then if I run it through terser, it'll go back down to 109kb. The speed was phenomenal - even when running through the separate terser step, it's so much faster.
- kaleidawave 6y agoAwesome benchmarks. Would be interesting how much minification affects actual client and server performance? And as understand all these tools use a lexing and ast parsing stage. It would be interesting if you could build a very simple minifier (at least a whitespace one) that could skip that step through just working on input buffer? There is also the fact that more code means longer minification time. You can praise esbuild for building a CRA in 2 seconds but there are workflows/frameworks that compile sub second by using less runtime code.
- Xevi 6y agoI wonder why SWC is so far behind the rest in terms of minified size. Kinda makes me not want to use it to be honest. Maybe there's some low hanging problems to tackle to make it better?
- CodesInChaos 6y agoIt looks like SWC will add a port of terser soonish: https://github.com/swc-project/swc/pull/1302 https://github.com/swc-project/swc/pull/1302
- cromwellian 6y agoClosure Compiler's advanced mode, besides having the best minification (it is actually a full compiler with many different optimization passes) and being slow, has a feature that often goes untested externally (but is used heavily inside of Google) and that's cross-module-code-motion. So if you split your code into modules, some of which are loaded later, the compiler can figure out which code in the initial download is used, but not yet and move it into the late loaded modules, whereas ordinarily this code gets retained in the initial load, because it looks like it is referenced. A long time ago Malte Ubl demonstrated this with the Splittable add on for Babel. https://medium.com/@cramforce/introducing-splittable-1c882babca7e https://medium.com/@cramforce/introducing-splittable-1c882ba...
- jiofih 6y agoThis is available in Rollup (and webpack 5? 6?), and the new generation tools like Snowpack / Vite benefit from native imports, no bundling needed to start with.
- cromwellian 6y agoHow do they implement it? Closure has two passes: Cross Module Method Motion, and Cross Module Code Motion. Cross Module Method Motion can move prototype methods, it leaves behind a stub, and a late-load overwrites the stub. Cross Module Code Motion can move top level global scope stuff (e.g. functions) The latter in particular is sensitive to declaration order effects, so movement is tricky and requires global analysis.
- jiofih 6y agoRollup’s tree shaking should have the same result - the way I understand it, it puts all module exports in the same scope, renaming variables to avoid conflicts, and removing duplicates. Then while “shaking” the dependency tree, a function that is only used by another imported module will end up in one of the dynamically loaded parts, i.e. not the main bundle. Where exactly it was first imported is irrelevant for its location in the output files. For snowpack, it’s just a natural consequence of runtime imports.
- arp242 6y agoI mostly stopped minifying my assets; as these benchmarks show, by far the biggest size reduction is in the gzip compression; for example lodash goes from 543k to 97k by just gzipping it, which can be reduced further to ~25k but that's actually quite a small saving compared to just the compression alone. And even on very slow connections 50k doesn't take too long to load. The reason I don't really like doing it is that it obfuscates the code. Happened to discover a bug in production (which may not be easy to repro)? A lot easier to examine that if your files aren't minified. It also allows users to examine the scripts if they encounter bugs, which I've done on a number of occasions. It also simplifies the entire build process. I know about source maps, but many people don't ship them and it all seems quite complicated for very little savings. When I do minify, I usually just have a little script to remove the comments, which is often the biggest win with the smallest obfuscation. I suppose that if you have a lot of JavaScript (>1M) it has more value, but in a lot of cases I see people minifying 100k scripts for little benefit "because that's just what you do". My application ships a small 3K script people can add to their website which isn't minified on purpose (imagine being able to inspect what 3rd-party scripts do exactly!); IIRC minifying it would save 500 bytes or something, but I've had quite literally dozens of people ask me "why isn't it minified?!"
- lxe 6y agoYou can always serve source maps for debugging purposes. They only get loaded once you actually open up devtools and start poking around. You can also configure stack traces to use the source maps, but I do understand the complexity increase in doing all this.
- taf2 6y agoI like minifying as an extra step to syntax check evaluate the code before it's shipped - very much like a compiler it can find some bugs...
- arp242 6y agoYou don't need minification for that as such; you can add it to your test step (and probably also run a more advanced static analysis tool while you're at it). Also, I've also had minification introduce bugs; and while it's uncommon I'd rather not take the risk as these things are not easy to figure out.