UNPKG

enb

Version:

Faster BEM/BEViS assembler

257 lines (221 loc) 8.08 kB
/** * BuildProfiler * ====== */ var inherit = require('inherit'); var _ = require('lodash'); var percentile = require('percentile'); var rangem = require('rangem'); /** * BuildProfiler * ============ * * Накапливает знания о времени сборки таргетов. * @name BuildProfiler * @class */ module.exports = inherit(/** @lends BuildProfiler.prototype */ { /** * Конструктор. * @param {Object} buildTimes * @private */ __constructor: function (buildTimes) { this._buildTimes = buildTimes || {}; }, /** * Устанавливает время начала работы для указанного таргета. * Если время не указано, то будет записано текущее. * @param {String} targetName * @param {String} techName * @param {Number} [time] */ setStartTime: function (targetName, techName, time) { var targetTimes = this._buildTimes[targetName] || (this._buildTimes[targetName] = {}); targetTimes.techName = techName; targetTimes.startTime = time || Date.now(); }, /** * Устанавливает время окончания работы для указанного таргета. * Если время не указано, то будет записано текущее. * @param {String} targetName * @param {Number} [time] */ setEndTime: function (targetName, time) { if (this._buildTimes[targetName]) { this._buildTimes[targetName].endTime = time ? time : Date.now(); } }, /** * @param {String} targetName * @param {Object} targetsBuildTimes * @param {String[]} deps */ _calculateTargetBuildTime: function (targetName, deps) { var buildTimes = this._buildTimes; // информация о времени выполнения таргета var targetTimes = buildTimes[targetName]; // начало выполнения таргета (включая время ожидания) var totalStartTime = targetTimes.startTime; // окончание выполнения таргета (включая время ожидания) var totalEndTime = targetTimes.endTime; // длительность выполнения таргета (без времини ожидания) var selfTime = 0; // отрезки чистого времени выполнения таргета (без времени ожидания) var timeline = []; if (deps.length) { var totalRange = { from: totalStartTime, to: totalEndTime }; var depRanges = deps.map(function (depTarget) { var times = buildTimes[depTarget]; return { from: times.startTime, to: times.endTime }; }); var selfRanges = rangem.subtract(totalRange, rangem.union(depRanges)); selfTime = rangesDuration(selfRanges); timeline = rangesToTimeline(selfRanges); } else { selfTime = totalEndTime - totalStartTime; timeline = [{ startTime: totalStartTime, endTime: totalEndTime }]; } var totalTime = totalEndTime - totalStartTime; var benchmarkObj = { totalTime: totalTime, selfTime: selfTime, watingTime: totalTime - selfTime, timeline: timeline }; this._buildTimes[targetName] = _.assign(this._buildTimes[targetName], benchmarkObj); }, /** * Вычисляет время выполнения каждого таргета. * @param {BuildGraph} graph * @returns {Object} */ calculateBuildTimes: function (graph) { var _this = this; Object.keys(this._buildTimes).map(function (targetName) { var deps = graph.getDirectDeps(targetName); _this._calculateTargetBuildTime(targetName, deps); }); return _.map(this._buildTimes, function (targetBuildInfo, target) { return _.assign({ target: target }, targetBuildInfo); }) .sort(function (a, b) { return b.selfTime - a.selfTime; }); }, /** * @param {Object} buildTimes * @returns {Array} */ calculateTechMetrics: function (buildTimes) { var metricsByTech = {}; _.map(buildTimes, function (benchmark, target) { var tech = benchmark && benchmark.techName; var targetBuildTimes = buildTimes[target]; if (!tech) { return; } var techMetrics = metricsByTech[tech] || { callNumber: 0, ranges: [], buildTimes: [] }; techMetrics.callNumber++; techMetrics.buildTimes.push(targetBuildTimes.selfTime); techMetrics.ranges = techMetrics.ranges.concat(timelineToRanges(targetBuildTimes.timeline)); metricsByTech[tech] = techMetrics; }); var totalTime = 0; var techMetrics = _.map(metricsByTech, function (metrics, tech) { var ranges = rangem.union(metrics.ranges); var buildTime = rangesDuration(ranges); totalTime += buildTime; return { tech: tech, callNumber: metrics.callNumber, buildTime: buildTime, buildTimes: metrics.buildTimes }; }); return _.map(techMetrics, function (metrics) { return _.assign(metrics, { buildTimePercent: calcPercent(metrics.buildTime, totalTime) }); }) .sort(function (a, b) { return b.buildTime - a.buildTime; }); }, /** * @param {Object} techMetrics * @param {Number[]]} percentileRanks * * @returns {{tech: String, percentiles: {rank: Number, value: Number}[]}[]} */ calculateTechPercentiles: function (techMetrics, percentileRanks) { var sortedPercentileRanks = percentileRanks.sort(function (a, b) { return a - b; }); var maxPercentileRankIndex = sortedPercentileRanks.length - 1; if (percentileRanks.length === 0) { return []; } return techMetrics.map(function (techInfo) { return { tech: techInfo.tech, percentiles: sortedPercentileRanks.map(function (percentileRank) { return { rank: percentileRank, value: percentile(percentileRank, techInfo.buildTimes) }; }) }; }) .sort(function (tech1, tech2) { var percentiles1 = tech1.percentiles; var percentiles2 = tech2.percentiles; return percentiles2[maxPercentileRankIndex].value - percentiles1[maxPercentileRankIndex].value; }); } }); /** * @param {Number} buildTime * @param {Number} totalTime * @return {Number} */ function calcPercent(value, total) { return value > 0 ? parseFloat(value / total * 100, 2) : 0; } /** * @param {{from: Number, to: Number}[]} ranges * * @returns {{startTime: Number, endTime: Number}[]} */ function rangesToTimeline(ranges) { return ranges.map(function (range) { return { startTime: range.from, endTime: range.to }; }); } /** * @param {{startTime: Number, endTime: Number}[]} timeline * * @returns {{from: Number, to: Number}[]} */ function timelineToRanges(timeline) { return timeline.map(function (times) { return { from: times.startTime, to: times.endTime }; }); } /** * @param {{from: Number, to: Number}[]} ranges * * @returns {Number} */ function rangesDuration(ranges) { var duration = 0; ranges.forEach(function (range) { duration += range.to - range.from; }); return duration; }