units.ts 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. export function relativeChange(final: number, initial: number): number {
  2. return (final - initial) / initial;
  3. }
  4. /**
  5. * Contains both singular and plural forms of units because the backend currently
  6. * returns different units between profiles and measurements
  7. */
  8. export type ProfilingFormatterUnit =
  9. | 'nanosecond'
  10. | 'nanoseconds'
  11. | 'microsecond'
  12. | 'microseconds'
  13. | 'millisecond'
  14. | 'milliseconds'
  15. | 'second'
  16. | 'seconds'
  17. | 'count';
  18. const durationMappings: Record<ProfilingFormatterUnit, number> = {
  19. nanosecond: 1e-9,
  20. nanoseconds: 1e-9,
  21. microsecond: 1e-6,
  22. microseconds: 1e-6,
  23. millisecond: 1e-3,
  24. milliseconds: 1e-3,
  25. second: 1,
  26. seconds: 1,
  27. count: 1,
  28. };
  29. export function makeFormatTo(
  30. from: ProfilingFormatterUnit | string,
  31. to: ProfilingFormatterUnit | string
  32. ) {
  33. if (durationMappings[from] === undefined) {
  34. throw new Error(`Cannot format unit ${from}, duration mapping is not defined`);
  35. }
  36. if (durationMappings[to] === undefined) {
  37. throw new Error(`Cannot format unit ${from}, duration mapping is not defined`);
  38. }
  39. if (from === to) {
  40. return (v: number) => {
  41. return v;
  42. };
  43. }
  44. return function format(v: number) {
  45. return formatTo(v, from, to);
  46. };
  47. }
  48. export function formatTo(
  49. v: number,
  50. from: ProfilingFormatterUnit | string,
  51. to: ProfilingFormatterUnit | string
  52. ) {
  53. const fromMultiplier = Math.log10(durationMappings[from]);
  54. const toMultiplier = Math.log10(durationMappings[to]);
  55. const value = v * Math.pow(10, fromMultiplier - toMultiplier);
  56. return value;
  57. }
  58. const format = (v: number, abbrev: string, precision: number) => {
  59. if (v === 0) {
  60. return '0' + abbrev;
  61. }
  62. return v.toFixed(precision) + abbrev;
  63. };
  64. export function makeFormatter(
  65. from: ProfilingFormatterUnit | string
  66. ): (value: number) => string {
  67. const DEFAULT_PRECISION = 2;
  68. const multiplier = durationMappings[from];
  69. if (multiplier === undefined) {
  70. throw new Error(`Cannot format unit ${from}, duration mapping is not defined`);
  71. }
  72. if (from === 'count') {
  73. return (value: number) => {
  74. return value.toFixed(0);
  75. };
  76. }
  77. return (value: number) => {
  78. const duration = value * multiplier;
  79. if (duration >= 1) {
  80. return format(duration, 's', DEFAULT_PRECISION);
  81. }
  82. if (duration / 1e-3 >= 1) {
  83. return format(duration / 1e-3, 'ms', DEFAULT_PRECISION);
  84. }
  85. if (duration / 1e-6 >= 1) {
  86. return format(duration / 1e-6, 'μs', DEFAULT_PRECISION);
  87. }
  88. return format(duration / 1e-9, 'ns', DEFAULT_PRECISION);
  89. };
  90. }
  91. function pad(n: number, slots: number) {
  92. return Math.floor(n).toString().padStart(slots, '0');
  93. }
  94. export function makeTimelineFormatter(from: ProfilingFormatterUnit | string) {
  95. const multiplier = durationMappings[from];
  96. if (multiplier === undefined) {
  97. throw new Error(`Cannot format unit ${from}, duration mapping is not defined`);
  98. }
  99. return (value: number) => {
  100. const s = Math.abs(value * multiplier);
  101. const m = s / 60;
  102. const ms = s * 1e3;
  103. return `${value < 0 ? '-' : ''}${pad(m, 2)}:${pad(s % 60, 2)}.${pad(ms % 1e3, 3)}`;
  104. };
  105. }