UNPKG

@acromedia/sloth

Version:

Resource profiler for node, utilizing child processes

156 lines (155 loc) 5.93 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const colors_1 = require("colors"); const fs_1 = __importDefault(require("fs")); const path_1 = __importDefault(require("path")); const asciichart_1 = require("asciichart"); const createChart_1 = __importDefault(require("../helpers/createChart")); function bytes(val) { const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB']; if (val === 0) { return '0 Bytes'; } const i = Math.floor(Math.log(val) / Math.log(1024)); if (i === 0) { return `${val} ${sizes[i]}`; } return `${val / parseFloat((1024 ** i).toFixed(2))} ${sizes[i]}`; } class ProfileResults { /** * Wraps the results of the Profiler into a class. * * Makes it easy to implement functions that * interact with the data. * * It is also helpful for providing useful jsdoc * definitions for the properties of the data. */ constructor(data) { if (!data) throw Error('Results data was null'); this.data = data; } /** * Get average memory usage. */ averageMemoryUsage() { let total = 0; this.data.mem_list.forEach((m) => { total += m; }); return total / this.data.mem_list.length; } /** * Get median memory usage. */ medianMemoryUsage() { return this.data.mem_list.sort((a, b) => a - b)[Math.round(this.data.mem_list.length / 2)]; } /** * Get most frequently occuring value in memory usage. */ modeMemoryUsage() { const freqMap = {}; let maxCount = 0; let largest = 0; this.data.mem_list.forEach((m) => { if (!freqMap[m]) freqMap[m] = 1; else freqMap[m] += 1; if (freqMap[m] > maxCount) { maxCount = freqMap[m]; largest = m; } }); return largest; } /** * Get the amount of memory taken up at a certain point within the test. * * @param {Number} ms */ memoryAtElapsed(ms) { if (ms > this.data.time_elapsed) throw new Error('Time provided was greater than total profile time.'); return this.data.mem_list[Math.round(ms / this.data.timestep_ms)]; } /** * Save data as a snapshot for comparison in future tests. */ saveSnapshot(filename, path = path_1.default.join(path_1.default.resolve('.'), '__snapshots__')) { if (!filename) throw Error('You must provide a file name.'); if (!fs_1.default.existsSync(path)) { fs_1.default.mkdirSync(path); } // Mostly just for user reference, not checked in code at all this.data.last_updated = new Date().toLocaleString(); return fs_1.default.writeFileSync(`${path + filename}.json`, JSON.stringify(this.data), 'utf8'); } compareToSnapshot(path, options = {}) { let obj; try { obj = JSON.parse(fs_1.default.readFileSync(path).toString()); } catch (e) { // Create a new snapshot an compare to that () const filename = path.split('/')[path.split('/').length - 1].split('.')[0]; const folderPath = path.split(`${filename}.json`)[0]; this.saveSnapshot(filename, folderPath); return { time_elapsed: 0, start_usage_bytes: 0, peak_usage_bytes: 0, end_usage_bytes: 0, }; } const comparison = { time_elapsed: this.data.time_elapsed - obj.time_elapsed, start_usage_bytes: this.data.start_usage_bytes - obj.start_usage_bytes, peak_usage_bytes: this.data.peak_usage_bytes - obj.peak_usage_bytes, end_usage_bytes: this.data.end_usage_bytes - obj.end_usage_bytes, }; if (options.logResultsDiff) { Object.keys(comparison).forEach((k) => { const isLess = comparison[k] > 0; const color = isLess ? colors_1.red : colors_1.green; const symbol = isLess ? '+' : '-'; if (k === 'time_elapsed') { console.log(`Time elapsed: ${color(`${String(comparison[k])}ms`)}`); return; } console.log(`${(k[0].toUpperCase() + k.substr(1)).replace(/_/g, ' ')}: ${color(`${symbol}${bytes(Number(isLess ? comparison[k] : -comparison[k]))}`)}`); }); } if (options.graph) { const chartData = [ this.data.mem_list.map((n) => n / 1024), obj.mem_list.map((n) => n / 1024), ]; if (options.graph === 'text') { // Graph memory chart console.log((0, asciichart_1.plot)(chartData.sort((a, b) => a.length - b.length), { height: 10, colors: [ this.data.mem_list.length > obj.mem_list.length ? asciichart_1.red : asciichart_1.blue, this.data.mem_list.length > obj.mem_list.length ? asciichart_1.blue : asciichart_1.red, ], }), '\nBlue - Current Run\nRed - Snapshot Run'); } else if (options.graph === 'image') { const imageName = `${Date.now()}.svg`; const imagePath = options.graph_path ? `${options.graph_path}/${imageName}` : `${path_1.default.resolve('.')}/${imageName}`; (0, createChart_1.default)(chartData, imagePath); console.log(`Memory usage graph saved to ${imagePath}!`); } } return comparison; } } exports.default = ProfileResults;