Optimizing static bundle size?
See original GitHub issueThe static bundling in browserify 2 looks very promising. However, one of my concerns is that it will increase the code size of the generated bundle as compared to simple concatentation, due to the boilerplate for each require’able file.
I expect it is possible to reduce the size of generated static bundle to levels comparable to concatentation, but this might break the design goals of browserify (say by not allowing bundled files to be require’d from outside the bundle). So I wanted to see your thoughts on this issue before I considered taking a crack at it myself.
As a contrived example, consider the following file, length.js:
module.exports = function(array) {
return array.length;
};
Ignoring the fixed overhead (196 bytes) for the bundle, the incremental size of just this file minified in the static bundle is about 61 bytes:
1:[function(_,m){m.exports=function(a){return a.length}},{}],
In a non-browserify world, the length function might instead be implemented as:
function length(array) {
return array.length;
}
Which minifies to 30 bytes:
function l(a){return a.length}
So, for the purpose of discussion, we can estimate that there is a per-file overhead of about 30 bytes when using static bundling versus concatenation. (Of course, building custom bundles using only the needed code via static analysis would produce a bundle that is far smaller than concatenating everything, but for this discussion I’m concerned with the default case, say where d3js.org provides a pre-built bundle with default functionality for convenience.)
If I extend this 30 bytes to D3’s 200 or so separate files, the resulting overhead is about 6KB, which represents about a 5% overhead on top of the 124KB d3.min.js. This isn’t huge, but at the same time, if I can avoid the overhead, that would make me happy.
One approach to reducing the size of the static bundle is to try to make it equivalent to concatenation. This might not be possible with certain edge cases, such as circular require’s, but it wouldn’t be hard for a common usage pattern. For example, the minified length.js in the bundle might appear as:
L=function(a){return a.length};
This is only 31 bytes, identical to concatenation. And subsequently, all instances of require("length")
would be replaced inline with L
. (In practice, browserify could use long names for each required module, such as _require_length, and then uglifyjs could reduce them to minimal variable names without collision.) Assuming that all bundled modules are listed in dependency-order, this should function equivalently to the current approach, but be quite a bit smaller.
I guess the biggest downside of this approach is that non-exported local variables in the file would need to be namespaced so as to avoid leaking into other modules. This might be challenging to implement and would further increase the size of the non-minified bundle.
Anyway, curious to hear your thoughts. I might still be willing to go-ahead given all the other benefits, but I always like to have my cake and eat it too.
Issue Analytics
- State:
- Created 11 years ago
- Comments:27 (6 by maintainers)
Top GitHub Comments
Here is a tool for converting
require('pathname')
torequire(integer)
to save space: intreq. It might be good to do this automatically in--standalone
mode to save bytes.gzip can reduce to overhead of repeating patterns like
module.exports=
very effectively. This would be premature optimizing in my opinion.