Pre-compress badge templates with SVGO

Prior to this patch, time spend in badge.js (computing text width and
compressing the SVG with SVGO) averaged 8.4 ms.

After this patch, it clocks at 2.4 ms on average.

Assuming this is the CPU bottleneck, it means that servers used to only be able
to handle 119 req/s (empirically, it is closer to 100 req/s). It should now
handle 416 req/s (although a better guess is at 1/0.004 = 250 req/s).
This commit is contained in:
Thaddee Tyl 2017-02-24 10:13:49 +01:00
parent 38cf7a60b5
commit 2f97be9118
2 changed files with 47 additions and 8 deletions

View File

@ -14,7 +14,51 @@ templateFiles.forEach(function(filename) {
path.join(__dirname, 'templates', filename)).toString(); path.join(__dirname, 'templates', filename)).toString();
var extension = path.extname(filename).slice(1); var extension = path.extname(filename).slice(1);
var style = filename.slice(0, -(('-template.' + extension).length)); var style = filename.slice(0, -(('-template.' + extension).length));
// Compile the template. Necessary to always have a working template.
templates[style + '-' + extension] = dot.template(templateData); templates[style + '-' + extension] = dot.template(templateData);
if (extension === 'svg') {
// Substitute dot code.
var mapping = new Map();
var mappingIndex = 1;
var untemplatedSvg = templateData.replace(/{{.*?}}/g, function(match) {
// Weird substitution that currently works for all templates.
var mapKey = '99999990' + mappingIndex + '.1';
mappingIndex++;
mapping.set(mapKey, match);
return mapKey;
});
compressSvg(untemplatedSvg, function(object) {
if (object.error !== undefined) {
console.error('Template ' + filename + ': ' + object.error + '\n' +
' Generated untemplated SVG:\n' +
'---\n' + untemplatedSvg + '---\n');
return;
}
// Substitute dot code back.
var svg = object.data;
var unmappedKeys = [];
mapping.forEach(function(value, key) {
var keySubstituted = false;
svg = svg.replace(RegExp(key, 'g'), function() {
keySubstituted = true;
return value;
});
if (!keySubstituted) {
unmappedKeys.push(key);
}
});
if (unmappedKeys.length > 0) {
console.error('Template ' + filename + ' has unmapped keys ' +
unmappedKeys.join(', ') + '.\n' +
' Generated untemplated SVG:\n' +
'---\n' + untemplatedSvg + '\n---\n' +
' Generated template:\n' +
'---\n' + svg + '\n---\n');
return;
}
templates[style + '-' + extension] = dot.template(svg);
});
}
}); });
function escapeXml(s) { function escapeXml(s) {
@ -34,7 +78,7 @@ function addEscapers(data) {
var colorscheme = require(path.join(__dirname, 'colorscheme.json')); var colorscheme = require(path.join(__dirname, 'colorscheme.json'));
function optimize(string, callback) { function compressSvg(string, callback) {
var svgo = new SVGO(); var svgo = new SVGO();
svgo.optimize(string, callback); svgo.optimize(string, callback);
} }
@ -95,12 +139,7 @@ function makeImage(data, cb) {
return; return;
} }
if (data.format === 'json') { cb(result);
cb(result);
} else {
// Run the SVG through SVGO.
optimize(result, function(object) { cb(object.data); });
}
} }
module.exports = makeImage; module.exports = makeImage;

View File

@ -1,5 +1,5 @@
{{it.widths[1]-=4;}}
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="{{=it.widths[0]+it.widths[1]+7}}" height="20"> <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="{{=it.widths[0]+it.widths[1]+7}}" height="20">
{{it.widths[1]-=4;}}
<style type="text/css"><![CDATA[ <style type="text/css"><![CDATA[
#llink:hover { fill:url(#b); stroke:#ccc; } #llink:hover { fill:url(#b); stroke:#ccc; }
#rlink:hover { fill:#4183C4; } #rlink:hover { fill:#4183C4; }

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB