Skip to content

Instantly share code, notes, and snippets.

@diurnalist
Last active August 29, 2015 14:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save diurnalist/2f6d349da3684bf40b9e to your computer and use it in GitHub Desktop.
Save diurnalist/2f6d349da3684bf40b9e to your computer and use it in GitHub Desktop.
Effects of GZip Compression on Randomly-generated CSS Sheets
#!/env/node
/**
* Measure the effect of "bloat" (repeated property chunks in a stylesheet, as might
* be introduced by a preprocessor and @mixin directives) in a randomly generated
* style sheet. Uses a small pool of property names and possible values for the purposes
* of the benchmark. Can configure the "bloat" factor by tweaking DUPE_FACTOR. That
* factor determines what % of the rules generated will be treated as repeatable,
* mixin-like rules.
*
* Generates two style sheets from the same input rules and compares sizes. For simplicity,
* when testing the "bloat" case, the bloat is added simply by concatenating the original
* rule with a rule that was marked as repeatable.
*/
var zlib = require('zlib'),
DUPE_FACTOR = 0.1,
NUM_RULES = [100, 400], // range
properties = {
'color': ['red', 'rgba(0, 255, 0, .25)', '#ffaa00'],
'font-family': ['Helvetical, Arial, sans-serif', 'Georgia, "Times New Roman", serif'],
'left': ['-10px', '0.1em', '200px', '0'],
'margin': ['0 auto', '10px 25px 0 25px', '-113px 0 0 0'],
'padding': ['25px 0', '-10px 0 10px 100px', 'auto'],
'position': ['absolute', 'relative', 'fixed'],
'text-size': ['25em', '12px', '28px']
},
css;
function rand (max) {
return Math.round(Math.random() * max);
}
function generateRule () {
var selector = [rand(16).toString(16), rand(32).toString(16)].join('-'),
propNames = Object.keys(properties).sort(function () { return rand(2) - 1 }),
numProperties,
possibleValues,
props = [];
numProperties = rand(propNames.length - 1) + 1;
for (var i = 0; i < numProperties; i++) {
possibleValues = properties[propNames[i]];
props.push([propNames[i], possibleValues[rand(possibleValues.length - 1)]].join(': '));
}
return [selector, props];
}
function selectorToCSSString (selector, props) {
return ['.', selector, ' {\n',
props.map(function (rule) {
return ['\t', rule, ';'].join('');
}).join('\n'),
'\n}\n'
].join('');
}
function generateCSS () {
var numRules = rand(NUM_RULES[1] - NUM_RULES[0]) + NUM_RULES[0],
numToRepeat = Math.round(numRules * DUPE_FACTOR),
rules = [],
repeatableRules = [],
withoutExtends = '',
withExtends = '',
rule;
while (rules.length < numRules) {
rule = generateRule();
rules.push(rule);
if (repeatableRules.length < numToRepeat) {
repeatableRules.push(rule);
}
}
rules.forEach(function (rule) {
var selector = rule[0],
props = rule[1];
withoutExtends += selectorToCSSString(selector, props);
withExtends += selectorToCSSString(selector, props.concat(repeatableRules[rand(repeatableRules.length - 1)]));
});
return {
a: new Buffer(withoutExtends),
b: new Buffer(withExtends)
};
}
function sizeReport (a, b) {
var diff = b.length - a.length;
return [
a.length,
' vs. ',
b.length,
' (diff = ',
diff,
' bytes, or ',
Math.round((diff / a.length) * 100),
'% larger)'
].join('');
}
css = generateCSS();
// Compare file size of w/ and w/o extra "bloat"
process.stdout.write('Using ' + (DUPE_FACTOR * 100) + '% duplication (bloat).\n');
process.stdout.write('Sizes (not gzipped): ' + sizeReport(css.a, css.b) + '\n');
zlib.gzip(css.a, function (err, aGzipped) {
zlib.gzip(css.b, function (err, bGzipped) {
process.stdout.write('Sizes (gzipped): ' + sizeReport(aGzipped, bGzipped) + '\n');
});
});
Using 10% duplication (bloat).
Sizes (not gzipped): 12866 vs. 23718 (diff = 10852 bytes, or 84% larger)
Sizes (gzipped): 1356 vs. 1780 (diff = 424 bytes, or 31% larger)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment