profiling.ts 1.9 KB

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