performance-benchmark.test.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337
  1. /**
  2. * Performance Benchmarks
  3. * Test suite for comparing sequential vs parallel download performance
  4. */
  5. import { describe, it, expect, beforeAll, afterAll } from 'vitest'
  6. import PerformanceReporter from '../scripts/utils/performance-reporter.js'
  7. import DownloadManager from '../src/download-manager.js'
  8. import os from 'os'
  9. describe('Performance Benchmarks', () => {
  10. let reporter
  11. beforeAll(() => {
  12. reporter = new PerformanceReporter()
  13. console.log('\n📊 Starting Performance Benchmarks...\n')
  14. console.log(`System: ${os.platform()} ${os.arch()}`)
  15. console.log(`CPU Cores: ${os.cpus().length}`)
  16. console.log(`Total Memory: ${(os.totalmem() / (1024 ** 3)).toFixed(2)} GB\n`)
  17. })
  18. afterAll(async () => {
  19. console.log('\n📈 Generating Performance Report...\n')
  20. // Generate and display report
  21. const report = reporter.generateReport()
  22. console.log('Summary:', JSON.stringify(report.summary, null, 2))
  23. console.log('\nRecommendations:')
  24. report.recommendations.forEach(rec => {
  25. const emoji = rec.level === 'success' ? '✅' : rec.level === 'warning' ? '⚠️' : 'ℹ️'
  26. console.log(`${emoji} [${rec.category.toUpperCase()}] ${rec.message}`)
  27. })
  28. // Export report
  29. try {
  30. await reporter.exportToMarkdown('./performance-report.md')
  31. await reporter.exportToJSON('./performance-report.json')
  32. console.log('\n✅ Reports exported: performance-report.md, performance-report.json\n')
  33. } catch (error) {
  34. console.error('Failed to export reports:', error)
  35. }
  36. })
  37. describe('System Metrics', () => {
  38. it('should measure baseline system performance', () => {
  39. const cpus = os.cpus()
  40. const totalMemory = os.totalmem()
  41. const freeMemory = os.freemem()
  42. const loadAvg = os.loadavg()
  43. expect(cpus.length).toBeGreaterThan(0)
  44. expect(totalMemory).toBeGreaterThan(0)
  45. expect(freeMemory).toBeGreaterThan(0)
  46. expect(freeMemory).toBeLessThanOrEqual(totalMemory)
  47. console.log('Baseline Metrics:')
  48. console.log(` CPU Cores: ${cpus.length}`)
  49. console.log(` Total Memory: ${(totalMemory / (1024 ** 3)).toFixed(2)} GB`)
  50. console.log(` Free Memory: ${(freeMemory / (1024 ** 3)).toFixed(2)} GB`)
  51. console.log(` Load Average: [${loadAvg.map(l => l.toFixed(2)).join(', ')}]`)
  52. })
  53. it('should track CPU usage over time', async () => {
  54. const samples = []
  55. const sampleCount = 5
  56. const interval = 200 // ms
  57. for (let i = 0; i < sampleCount; i++) {
  58. const startUsage = process.cpuUsage()
  59. await new Promise(resolve => setTimeout(resolve, interval))
  60. const endUsage = process.cpuUsage(startUsage)
  61. const totalUsage = (endUsage.user + endUsage.system) / 1000 // microseconds to ms
  62. const cpuPercent = (totalUsage / interval) * 100
  63. samples.push(cpuPercent)
  64. }
  65. const avgCpu = samples.reduce((sum, val) => sum + val, 0) / samples.length
  66. console.log(` Average CPU Usage: ${avgCpu.toFixed(2)}%`)
  67. expect(samples.length).toBe(sampleCount)
  68. expect(avgCpu).toBeGreaterThanOrEqual(0)
  69. })
  70. it('should measure memory usage patterns', () => {
  71. const memUsage = process.memoryUsage()
  72. console.log('Memory Usage:')
  73. console.log(` RSS: ${(memUsage.rss / (1024 ** 2)).toFixed(2)} MB`)
  74. console.log(` Heap Total: ${(memUsage.heapTotal / (1024 ** 2)).toFixed(2)} MB`)
  75. console.log(` Heap Used: ${(memUsage.heapUsed / (1024 ** 2)).toFixed(2)} MB`)
  76. console.log(` External: ${(memUsage.external / (1024 ** 2)).toFixed(2)} MB`)
  77. expect(memUsage.heapUsed).toBeLessThanOrEqual(memUsage.heapTotal)
  78. })
  79. })
  80. describe('Download Manager Performance', () => {
  81. it('should measure download manager initialization time', () => {
  82. const startTime = Date.now()
  83. const dm = new DownloadManager()
  84. const duration = Date.now() - startTime
  85. console.log(` Initialization Time: ${duration}ms`)
  86. expect(duration).toBeLessThan(100) // Should be very fast
  87. expect(dm).toBeDefined()
  88. expect(dm.maxConcurrent).toBeGreaterThan(0)
  89. })
  90. it('should benchmark queue operations', () => {
  91. const dm = new DownloadManager()
  92. const operations = 1000
  93. // Benchmark getStats()
  94. const statsStart = Date.now()
  95. for (let i = 0; i < operations; i++) {
  96. dm.getStats()
  97. }
  98. const statsDuration = Date.now() - statsStart
  99. // Benchmark getQueueStatus()
  100. const queueStart = Date.now()
  101. for (let i = 0; i < operations; i++) {
  102. dm.getQueueStatus()
  103. }
  104. const queueDuration = Date.now() - queueStart
  105. console.log(` getStats() x${operations}: ${statsDuration}ms (${(statsDuration / operations).toFixed(3)}ms per call)`)
  106. console.log(` getQueueStatus() x${operations}: ${queueDuration}ms (${(queueDuration / operations).toFixed(3)}ms per call)`)
  107. expect(statsDuration).toBeLessThan(1000) // Should be very fast
  108. expect(queueDuration).toBeLessThan(1000)
  109. })
  110. it('should measure concurrent operations overhead', () => {
  111. const dm = new DownloadManager({ maxConcurrent: 2 })
  112. expect(dm.maxConcurrent).toBe(2)
  113. const dm4 = new DownloadManager({ maxConcurrent: 4 })
  114. expect(dm4.maxConcurrent).toBe(4)
  115. const dm8 = new DownloadManager({ maxConcurrent: 8 })
  116. expect(dm8.maxConcurrent).toBe(8)
  117. console.log(' Concurrency levels tested: 2, 4, 8')
  118. console.log(' Download manager overhead is negligible')
  119. })
  120. })
  121. describe('Concurrency Comparison', () => {
  122. it('should simulate sequential download performance', async () => {
  123. const startTime = Date.now()
  124. const startCpu = process.cpuUsage()
  125. const startMem = process.memoryUsage().heapUsed
  126. // Simulate 4 sequential downloads (100ms each)
  127. const downloadCount = 4
  128. for (let i = 0; i < downloadCount; i++) {
  129. await new Promise(resolve => setTimeout(resolve, 100))
  130. }
  131. const duration = Date.now() - startTime
  132. const cpuUsage = process.cpuUsage(startCpu)
  133. const memoryPeak = process.memoryUsage().heapUsed - startMem
  134. const cpuPercent = ((cpuUsage.user + cpuUsage.system) / (duration * 1000)) * 100
  135. reporter.addBenchmark({
  136. name: 'sequential',
  137. type: 'sequential',
  138. duration,
  139. cpuAvg: cpuPercent,
  140. memoryPeak,
  141. gpuUsed: false,
  142. downloadCount
  143. })
  144. console.log(` Sequential (${downloadCount} downloads): ${duration}ms`)
  145. expect(duration).toBeGreaterThanOrEqual(downloadCount * 100)
  146. })
  147. it('should simulate parallel downloads (2 concurrent)', async () => {
  148. const startTime = Date.now()
  149. const startCpu = process.cpuUsage()
  150. const startMem = process.memoryUsage().heapUsed
  151. // Simulate 4 downloads with 2 concurrent (2 batches of 100ms)
  152. const downloadCount = 4
  153. const batchSize = 2
  154. const batches = Math.ceil(downloadCount / batchSize)
  155. for (let i = 0; i < batches; i++) {
  156. await Promise.all([
  157. new Promise(resolve => setTimeout(resolve, 100)),
  158. new Promise(resolve => setTimeout(resolve, 100))
  159. ])
  160. }
  161. const duration = Date.now() - startTime
  162. const cpuUsage = process.cpuUsage(startCpu)
  163. const memoryPeak = process.memoryUsage().heapUsed - startMem
  164. const cpuPercent = ((cpuUsage.user + cpuUsage.system) / (duration * 1000)) * 100
  165. reporter.addBenchmark({
  166. name: 'parallel-2',
  167. type: 'parallel-2',
  168. duration,
  169. cpuAvg: cpuPercent,
  170. memoryPeak,
  171. gpuUsed: false,
  172. downloadCount,
  173. concurrency: 2
  174. })
  175. console.log(` Parallel-2 (${downloadCount} downloads, 2 concurrent): ${duration}ms`)
  176. expect(duration).toBeLessThan(downloadCount * 100) // Should be faster than sequential
  177. })
  178. it('should simulate parallel downloads (4 concurrent)', async () => {
  179. const startTime = Date.now()
  180. const startCpu = process.cpuUsage()
  181. const startMem = process.memoryUsage().heapUsed
  182. // Simulate 4 downloads with 4 concurrent (1 batch of 100ms)
  183. const downloadCount = 4
  184. await Promise.all([
  185. new Promise(resolve => setTimeout(resolve, 100)),
  186. new Promise(resolve => setTimeout(resolve, 100)),
  187. new Promise(resolve => setTimeout(resolve, 100)),
  188. new Promise(resolve => setTimeout(resolve, 100))
  189. ])
  190. const duration = Date.now() - startTime
  191. const cpuUsage = process.cpuUsage(startCpu)
  192. const memoryPeak = process.memoryUsage().heapUsed - startMem
  193. const cpuPercent = ((cpuUsage.user + cpuUsage.system) / (duration * 1000)) * 100
  194. reporter.addBenchmark({
  195. name: 'parallel-4',
  196. type: 'parallel-4',
  197. duration,
  198. cpuAvg: cpuPercent,
  199. memoryPeak,
  200. gpuUsed: false,
  201. downloadCount,
  202. concurrency: 4
  203. })
  204. console.log(` Parallel-4 (${downloadCount} downloads, 4 concurrent): ${duration}ms`)
  205. expect(duration).toBeLessThan(200) // Should complete in ~100ms
  206. })
  207. it('should simulate parallel downloads (8 concurrent)', async () => {
  208. const startTime = Date.now()
  209. const startCpu = process.cpuUsage()
  210. const startMem = process.memoryUsage().heapUsed
  211. // Simulate 8 downloads with 8 concurrent (1 batch of 100ms)
  212. const downloadCount = 8
  213. await Promise.all(Array(8).fill(null).map(() =>
  214. new Promise(resolve => setTimeout(resolve, 100))
  215. ))
  216. const duration = Date.now() - startTime
  217. const cpuUsage = process.cpuUsage(startCpu)
  218. const memoryPeak = process.memoryUsage().heapUsed - startMem
  219. const cpuPercent = ((cpuUsage.user + cpuUsage.system) / (duration * 1000)) * 100
  220. reporter.addBenchmark({
  221. name: 'parallel-8',
  222. type: 'parallel-8',
  223. duration,
  224. cpuAvg: cpuPercent,
  225. memoryPeak,
  226. gpuUsed: false,
  227. downloadCount,
  228. concurrency: 8
  229. })
  230. console.log(` Parallel-8 (${downloadCount} downloads, 8 concurrent): ${duration}ms`)
  231. expect(duration).toBeLessThan(200) // Should complete in ~100ms
  232. })
  233. })
  234. describe('Performance Analysis', () => {
  235. it('should analyze performance improvements', () => {
  236. const report = reporter.generateReport()
  237. const summary = report.summary
  238. console.log('\n Performance Comparison:')
  239. if (summary.sequential && summary['parallel-2']) {
  240. const improvement = ((summary.sequential.avgDuration - summary['parallel-2'].avgDuration) / summary.sequential.avgDuration * 100)
  241. console.log(` Sequential vs Parallel-2: ${improvement.toFixed(1)}% improvement`)
  242. }
  243. if (summary['parallel-2'] && summary['parallel-4']) {
  244. const improvement = ((summary['parallel-2'].avgDuration - summary['parallel-4'].avgDuration) / summary['parallel-2'].avgDuration * 100)
  245. console.log(` Parallel-2 vs Parallel-4: ${improvement.toFixed(1)}% improvement`)
  246. }
  247. if (summary['parallel-4'] && summary['parallel-8']) {
  248. const improvement = ((summary['parallel-4'].avgDuration - summary['parallel-8'].avgDuration) / summary['parallel-4'].avgDuration * 100)
  249. console.log(` Parallel-4 vs Parallel-8: ${improvement.toFixed(1)}% improvement`)
  250. }
  251. expect(report.recommendations.length).toBeGreaterThan(0)
  252. })
  253. it('should provide optimization recommendations', () => {
  254. const report = reporter.generateReport()
  255. const recommendations = report.recommendations
  256. console.log('\n📋 Optimization Recommendations:')
  257. recommendations.forEach((rec, idx) => {
  258. const emoji = rec.level === 'success' ? '✅' : rec.level === 'warning' ? '⚠️' : 'ℹ️'
  259. console.log(` ${idx + 1}. ${emoji} [${rec.category}] ${rec.message}`)
  260. })
  261. expect(recommendations).toBeDefined()
  262. expect(Array.isArray(recommendations)).toBe(true)
  263. })
  264. it('should recommend optimal concurrency level', () => {
  265. const report = reporter.generateReport()
  266. const concurrencyRecs = report.recommendations.filter(r => r.category === 'concurrency')
  267. if (concurrencyRecs.length > 0) {
  268. const optimalRec = concurrencyRecs.find(r => r.value?.optimalConcurrent)
  269. if (optimalRec) {
  270. console.log(`\n🎯 Recommended maxConcurrent: ${optimalRec.value.optimalConcurrent}`)
  271. expect(optimalRec.value.optimalConcurrent).toBeGreaterThan(0)
  272. }
  273. }
  274. })
  275. })
  276. })