profiler.js 2.1 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485
  1. "use strict";
  2. const fs = require("fs-extra");
  3. const npmlog = require("npmlog");
  4. const upath = require("upath");
  5. const hrtimeToMicroseconds = (hrtime) => {
  6. return (hrtime[0] * 1e9 + hrtime[1]) / 1000;
  7. };
  8. const range = (len) => {
  9. return Array(len)
  10. .fill()
  11. .map((_, idx) => idx);
  12. };
  13. const getTimeBasedFilename = () => {
  14. const now = new Date(); // 2011-10-05T14:48:00.000Z
  15. const datetime = now.toISOString().split(".")[0]; // 2011-10-05T14:48:00
  16. const datetimeNormalized = datetime.replace(/-|:/g, ""); // 20111005T144800
  17. return `Lerna-Profile-${datetimeNormalized}.json`;
  18. };
  19. /**
  20. * @typedef {object} ProfilerConfig
  21. * @property {number} concurrency
  22. * @property {typeof npmlog} [log]
  23. * @property {string} [outputDirectory]
  24. */
  25. /**
  26. * A profiler to trace execution times across multiple concurrent calls.
  27. */
  28. class Profiler {
  29. /**
  30. * @param {ProfilerConfig} options
  31. */
  32. constructor({ concurrency, log = npmlog, outputDirectory }) {
  33. this.events = [];
  34. this.logger = log;
  35. this.outputPath = upath.join(upath.resolve(outputDirectory || "."), getTimeBasedFilename());
  36. this.threads = range(concurrency);
  37. }
  38. run(fn, name) {
  39. let startTime;
  40. let threadId;
  41. return Promise.resolve()
  42. .then(() => {
  43. startTime = process.hrtime();
  44. threadId = this.threads.shift();
  45. })
  46. .then(() => fn())
  47. .then((value) => {
  48. const duration = process.hrtime(startTime);
  49. // Trace Event Format documentation:
  50. // https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/preview
  51. const event = {
  52. name,
  53. ph: "X",
  54. ts: hrtimeToMicroseconds(startTime),
  55. pid: 1,
  56. tid: threadId,
  57. dur: hrtimeToMicroseconds(duration),
  58. };
  59. this.events.push(event);
  60. this.threads.unshift(threadId);
  61. this.threads.sort();
  62. return value;
  63. });
  64. }
  65. output() {
  66. return fs
  67. .outputJson(this.outputPath, this.events)
  68. .then(() => this.logger.info("profiler", `Performance profile saved to ${this.outputPath}`));
  69. }
  70. }
  71. module.exports.Profiler = Profiler;