profiling.ts 2.2 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192
  1. /* oxlint-disable no-console */
  2. /* oxlint-disable no-restricted-globals */
  3. import { nextTick } from 'vue'
  4. declare global {
  5. var doProfile: boolean
  6. var reactivity: boolean
  7. var recordTime: boolean
  8. var times: Record<string, number[]>
  9. }
  10. globalThis.recordTime = true
  11. globalThis.doProfile = false
  12. globalThis.reactivity = false
  13. export const defer = () => new Promise(r => requestIdleCallback(r))
  14. const times: Record<string, number[]> = (globalThis.times = {})
  15. export function wrap(
  16. id: string,
  17. fn: (...args: any[]) => any,
  18. ): (...args: any[]) => Promise<void> {
  19. return async (...args) => {
  20. if (!globalThis.recordTime) {
  21. return fn(...args)
  22. }
  23. document.body.classList.remove('done')
  24. const { doProfile } = globalThis
  25. await nextTick()
  26. doProfile && console.profile(id)
  27. const start = performance.now()
  28. fn(...args)
  29. await nextTick()
  30. let time: number
  31. if (globalThis.reactivity) {
  32. time = performance.measure(
  33. 'flushJobs-measure',
  34. 'flushJobs-start',
  35. 'flushJobs-end',
  36. ).duration
  37. performance.clearMarks()
  38. performance.clearMeasures()
  39. } else {
  40. time = performance.now() - start
  41. }
  42. const prevTimes = times[id] || (times[id] = [])
  43. prevTimes.push(time)
  44. const { min, max, median, mean, std } = compute(prevTimes)
  45. const msg =
  46. `${id}: min: ${min} / ` +
  47. `max: ${max} / ` +
  48. `median: ${median}ms / ` +
  49. `mean: ${mean}ms / ` +
  50. `time: ${time.toFixed(2)}ms / ` +
  51. `std: ${std} ` +
  52. `over ${prevTimes.length} runs`
  53. doProfile && console.profileEnd(id)
  54. console.log(msg)
  55. const timeEl = document.getElementById('time')!
  56. timeEl.textContent = msg
  57. document.body.classList.add('done')
  58. }
  59. }
  60. function compute(array: number[]) {
  61. const n = array.length
  62. const max = Math.max(...array)
  63. const min = Math.min(...array)
  64. const mean = array.reduce((a, b) => a + b) / n
  65. const std = Math.sqrt(
  66. array.map(x => Math.pow(x - mean, 2)).reduce((a, b) => a + b) / n,
  67. )
  68. const median = array.slice().sort((a, b) => a - b)[Math.floor(n / 2)]
  69. return {
  70. max: round(max),
  71. min: round(min),
  72. mean: round(mean),
  73. std: round(std),
  74. median: round(median),
  75. }
  76. }
  77. function round(n: number) {
  78. return +n.toFixed(2)
  79. }