diff --git a/benchmarks/README.md b/benchmarks/README.md index 937ff94..cf8ce8d 100644 --- a/benchmarks/README.md +++ b/benchmarks/README.md @@ -1,150 +1,152 @@ # Benchmarks +> Machine: MacBook Pro 2021 (M1 Max, 32GB RAM, Node.js v16.14.0) + ```shell Starting benchmark all.js ╔══════════════════════╤══════════════════════╤══════════════════════╤══════════════════════╤══════════════════════╗ -║ @foxify/router │ 1,160,103 ops/sec │ 100% │ ±1.64% │ 89 runs sampled ║ +║ @foxify/router │ 2,076,237 ops/sec │ 100% │ ±0.25% │ 97 runs sampled ║ ╟──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────╢ -║ foxify (v0.10.20) │ 579,895 ops/sec │ 49.99% │ ±0.6% │ 95 runs sampled ║ +║ foxify (v0.10.20) │ 814,022 ops/sec │ 39.21% │ ±0.72% │ 97 runs sampled ║ ╟──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────╢ -║ @hapi/call │ 30,657 ops/sec │ 2.64% │ ±0.4% │ 88 runs sampled ║ +║ @hapi/call │ 29,125 ops/sec │ 1.4% │ ±0.99% │ 97 runs sampled ║ ╟──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────╢ -║ express │ 178,374 ops/sec │ 15.38% │ ±1.29% │ 90 runs sampled ║ +║ express │ 296,075 ops/sec │ 14.26% │ ±0.29% │ 100 runs sampled ║ ╟──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────╢ -║ find-my-way │ 726,984 ops/sec │ 62.67% │ ±0.68% │ 96 runs sampled ║ +║ find-my-way │ 1,085,710 ops/sec │ 52.29% │ ±0.13% │ 98 runs sampled ║ ╟──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────╢ -║ koa-router │ 314,120 ops/sec │ 27.08% │ ±0.56% │ 93 runs sampled ║ +║ koa-router │ 571,624 ops/sec │ 27.53% │ ±0.12% │ 98 runs sampled ║ ╟──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────╢ -║ koa-tree-router │ 1,337,954 ops/sec │ 115.33% │ ±0.38% │ 96 runs sampled ║ +║ koa-tree-router │ 2,047,634 ops/sec │ 98.62% │ ±0.33% │ 99 runs sampled ║ ╟──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────╢ -║ trek-router │ 1,051,333 ops/sec │ 90.62% │ ±0.4% │ 97 runs sampled ║ +║ trek-router │ 1,430,980 ops/sec │ 68.92% │ ±0.11% │ 98 runs sampled ║ ╚══════════════════════╧══════════════════════╧══════════════════════╧══════════════════════╧══════════════════════╝ -> Fastest is [ 'koa-tree-router' ] +> Fastest is [ '@foxify/router' ] Starting benchmark long-static.js ╔══════════════════════╤══════════════════════╤══════════════════════╤══════════════════════╤══════════════════════╗ -║ @foxify/router │ 20,998,015 ops/sec │ 100% │ ±1.88% │ 94 runs sampled ║ +║ @foxify/router │ 36,877,028 ops/sec │ 100% │ ±0.07% │ 101 runs sampled ║ ╟──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────╢ -║ foxify (v0.10.20) │ 5,577,952 ops/sec │ 26.56% │ ±0.37% │ 96 runs sampled ║ +║ foxify (v0.10.20) │ 8,293,120 ops/sec │ 22.49% │ ±0.07% │ 97 runs sampled ║ ╟──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────╢ -║ @hapi/call │ 4,639,987 ops/sec │ 22.1% │ ±0.42% │ 94 runs sampled ║ +║ @hapi/call │ 7,921,547 ops/sec │ 21.48% │ ±0.08% │ 102 runs sampled ║ ╟──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────╢ -║ express │ 1,003,189 ops/sec │ 4.78% │ ±0.51% │ 97 runs sampled ║ +║ express │ 1,700,775 ops/sec │ 4.61% │ ±0.24% │ 97 runs sampled ║ ╟──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────╢ -║ find-my-way │ 9,023,632 ops/sec │ 42.97% │ ±0.38% │ 95 runs sampled ║ +║ find-my-way │ 12,093,705 ops/sec │ 32.79% │ ±0.18% │ 99 runs sampled ║ ╟──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────╢ -║ koa-router │ 1,801,868 ops/sec │ 8.58% │ ±1.07% │ 95 runs sampled ║ +║ koa-router │ 4,018,329 ops/sec │ 10.9% │ ±0.11% │ 101 runs sampled ║ ╟──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────╢ -║ koa-tree-router │ 14,297,141 ops/sec │ 68.09% │ ±0.8% │ 94 runs sampled ║ +║ koa-tree-router │ 19,980,184 ops/sec │ 54.18% │ ±0.23% │ 97 runs sampled ║ ╟──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────╢ -║ trek-router │ 11,445,143 ops/sec │ 54.51% │ ±0.38% │ 97 runs sampled ║ +║ trek-router │ 15,636,531 ops/sec │ 42.4% │ ±0.12% │ 101 runs sampled ║ ╚══════════════════════╧══════════════════════╧══════════════════════╧══════════════════════╧══════════════════════╝ > Fastest is [ '@foxify/router' ] Starting benchmark dynamic-route.js ╔══════════════════════╤══════════════════════╤══════════════════════╤══════════════════════╤══════════════════════╗ -║ @foxify/router │ 5,187,123 ops/sec │ 100% │ ±0.73% │ 92 runs sampled ║ +║ @foxify/router │ 9,259,062 ops/sec │ 100% │ ±0.15% │ 99 runs sampled ║ ╟──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────╢ -║ foxify (v0.10.20) │ 2,507,681 ops/sec │ 48.34% │ ±1.21% │ 94 runs sampled ║ +║ foxify (v0.10.20) │ 3,494,942 ops/sec │ 37.75% │ ±0.15% │ 99 runs sampled ║ ╟──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────╢ -║ @hapi/call │ 95,289 ops/sec │ 1.84% │ ±0.44% │ 82 runs sampled ║ +║ @hapi/call │ 88,908 ops/sec │ 0.96% │ ±0.95% │ 100 runs sampled ║ ╟──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────╢ -║ express │ 1,167,981 ops/sec │ 22.52% │ ±0.32% │ 96 runs sampled ║ +║ express │ 1,867,355 ops/sec │ 20.17% │ ±0.25% │ 100 runs sampled ║ ╟──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────╢ -║ find-my-way │ 2,616,279 ops/sec │ 50.44% │ ±0.34% │ 98 runs sampled ║ +║ find-my-way │ 3,675,401 ops/sec │ 39.7% │ ±0.1% │ 101 runs sampled ║ ╟──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────╢ -║ koa-router │ 1,726,611 ops/sec │ 33.29% │ ±1.58% │ 92 runs sampled ║ +║ koa-router │ 4,036,965 ops/sec │ 43.6% │ ±0.08% │ 99 runs sampled ║ ╟──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────╢ -║ koa-tree-router │ 5,573,944 ops/sec │ 107.46% │ ±0.64% │ 88 runs sampled ║ +║ koa-tree-router │ 7,945,016 ops/sec │ 85.81% │ ±0.23% │ 99 runs sampled ║ ╟──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────╢ -║ trek-router │ 4,210,878 ops/sec │ 81.18% │ ±0.51% │ 94 runs sampled ║ +║ trek-router │ 5,128,430 ops/sec │ 55.39% │ ±0.11% │ 101 runs sampled ║ ╚══════════════════════╧══════════════════════╧══════════════════════╧══════════════════════╧══════════════════════╝ -> Fastest is [ 'koa-tree-router' ] +> Fastest is [ '@foxify/router' ] Starting benchmark static-same-radix.js ╔══════════════════════╤══════════════════════╤══════════════════════╤══════════════════════╤══════════════════════╗ -║ @foxify/router │ 9,619,379 ops/sec │ 100% │ ±2.66% │ 93 runs sampled ║ +║ @foxify/router │ 16,450,356 ops/sec │ 100% │ ±0.09% │ 100 runs sampled ║ ╟──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────╢ -║ foxify (v0.10.20) │ 4,824,968 ops/sec │ 50.16% │ ±1.3% │ 92 runs sampled ║ +║ foxify (v0.10.20) │ 6,618,706 ops/sec │ 40.23% │ ±0.28% │ 96 runs sampled ║ ╟──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────╢ -║ @hapi/call │ 95,994 ops/sec │ 1% │ ±0.44% │ 93 runs sampled ║ +║ @hapi/call │ 88,362 ops/sec │ 0.54% │ ±0.69% │ 97 runs sampled ║ ╟──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────╢ -║ express │ 1,975,876 ops/sec │ 20.54% │ ±0.35% │ 96 runs sampled ║ +║ express │ 3,568,400 ops/sec │ 21.69% │ ±0.27% │ 98 runs sampled ║ ╟──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────╢ -║ find-my-way │ 6,421,182 ops/sec │ 66.75% │ ±1.33% │ 91 runs sampled ║ +║ find-my-way │ 8,523,835 ops/sec │ 51.82% │ ±0.09% │ 99 runs sampled ║ ╟──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────╢ -║ koa-router │ 1,795,750 ops/sec │ 18.67% │ ±0.31% │ 98 runs sampled ║ +║ koa-router │ 4,120,907 ops/sec │ 25.05% │ ±0.07% │ 99 runs sampled ║ ╟──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────╢ -║ koa-tree-router │ 11,631,519 ops/sec │ 120.92% │ ±0.55% │ 93 runs sampled ║ +║ koa-tree-router │ 15,446,831 ops/sec │ 93.9% │ ±0.21% │ 99 runs sampled ║ ╟──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────╢ -║ trek-router │ 8,613,392 ops/sec │ 89.54% │ ±0.46% │ 93 runs sampled ║ +║ trek-router │ 11,284,350 ops/sec │ 68.6% │ ±0.12% │ 98 runs sampled ║ ╚══════════════════════╧══════════════════════╧══════════════════════╧══════════════════════╧══════════════════════╝ -> Fastest is [ 'koa-tree-router' ] +> Fastest is [ '@foxify/router' ] Starting benchmark short-static.js ╔══════════════════════╤══════════════════════╤══════════════════════╤══════════════════════╤══════════════════════╗ -║ @foxify/router │ 54,331,524 ops/sec │ 100% │ ±0.63% │ 91 runs sampled ║ +║ @foxify/router │ 70,268,967 ops/sec │ 100% │ ±0.31% │ 100 runs sampled ║ ╟──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────╢ -║ foxify (v0.10.20) │ 9,693,855 ops/sec │ 17.84% │ ±1.5% │ 93 runs sampled ║ +║ foxify (v0.10.20) │ 13,237,966 ops/sec │ 18.84% │ ±0.1% │ 100 runs sampled ║ ╟──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────╢ -║ @hapi/call │ 5,448,401 ops/sec │ 10.03% │ ±0.34% │ 96 runs sampled ║ +║ @hapi/call │ 8,411,191 ops/sec │ 11.97% │ ±0.11% │ 100 runs sampled ║ ╟──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────╢ -║ express │ 2,368,066 ops/sec │ 4.36% │ ±0.46% │ 98 runs sampled ║ +║ express │ 4,389,716 ops/sec │ 6.25% │ ±0.35% │ 98 runs sampled ║ ╟──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────╢ -║ find-my-way │ 20,689,309 ops/sec │ 38.08% │ ±0.58% │ 94 runs sampled ║ +║ find-my-way │ 27,638,291 ops/sec │ 39.33% │ ±0.1% │ 101 runs sampled ║ ╟──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────╢ -║ koa-router │ 1,937,856 ops/sec │ 3.57% │ ±0.36% │ 94 runs sampled ║ +║ koa-router │ 4,263,571 ops/sec │ 6.07% │ ±0.36% │ 99 runs sampled ║ ╟──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────╢ -║ koa-tree-router │ 32,034,147 ops/sec │ 58.96% │ ±2.19% │ 88 runs sampled ║ +║ koa-tree-router │ 45,572,944 ops/sec │ 64.86% │ ±0.55% │ 95 runs sampled ║ ╟──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────╢ -║ trek-router │ 19,322,016 ops/sec │ 35.56% │ ±0.66% │ 93 runs sampled ║ +║ trek-router │ 26,040,232 ops/sec │ 37.06% │ ±0.48% │ 100 runs sampled ║ ╚══════════════════════╧══════════════════════╧══════════════════════╧══════════════════════╧══════════════════════╝ > Fastest is [ '@foxify/router' ] Starting benchmark wildcard.js ╔══════════════════════╤══════════════════════╤══════════════════════╤══════════════════════╤══════════════════════╗ -║ @foxify/router │ 6,287,612 ops/sec │ 100% │ ±0.46% │ 95 runs sampled ║ +║ @foxify/router │ 11,340,834 ops/sec │ 100% │ ±0.15% │ 92 runs sampled ║ ╟──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────╢ -║ foxify (v0.10.20) │ 4,155,951 ops/sec │ 66.1% │ ±0.27% │ 93 runs sampled ║ +║ foxify (v0.10.20) │ 5,830,331 ops/sec │ 51.41% │ ±0.17% │ 100 runs sampled ║ ╟──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────╢ -║ @hapi/call │ 92,480 ops/sec │ 1.47% │ ±1.47% │ 81 runs sampled ║ +║ @hapi/call │ 88,103 ops/sec │ 0.78% │ ±0.91% │ 99 runs sampled ║ ╟──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────╢ -║ express │ 510,613 ops/sec │ 8.12% │ ±0.32% │ 96 runs sampled ║ +║ express │ 1,158,555 ops/sec │ 10.22% │ ±0.45% │ 101 runs sampled ║ ╟──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────╢ -║ find-my-way │ 5,045,755 ops/sec │ 80.25% │ ±0.4% │ 95 runs sampled ║ +║ find-my-way │ 6,775,974 ops/sec │ 59.75% │ ±0.26% │ 95 runs sampled ║ ╟──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────╢ -║ koa-router │ 1,696,414 ops/sec │ 26.98% │ ±0.3% │ 98 runs sampled ║ +║ koa-router │ 4,283,125 ops/sec │ 37.77% │ ±0.14% │ 101 runs sampled ║ ╟──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────╢ -║ koa-tree-router │ 7,460,308 ops/sec │ 118.65% │ ±1.25% │ 92 runs sampled ║ +║ koa-tree-router │ 10,712,640 ops/sec │ 94.46% │ ±0.27% │ 97 runs sampled ║ ╟──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────╢ -║ trek-router │ 7,452,064 ops/sec │ 118.52% │ ±0.33% │ 97 runs sampled ║ +║ trek-router │ 8,828,853 ops/sec │ 77.85% │ ±0.12% │ 101 runs sampled ║ ╚══════════════════════╧══════════════════════╧══════════════════════╧══════════════════════╧══════════════════════╝ -> Fastest is [ 'trek-router' ] +> Fastest is [ '@foxify/router' ] Starting benchmark mixed-static-dynamic.js ╔══════════════════════╤══════════════════════╤══════════════════════╤══════════════════════╤══════════════════════╗ -║ @foxify/router │ 6,209,936 ops/sec │ 100% │ ±0.5% │ 93 runs sampled ║ +║ @foxify/router │ 10,989,532 ops/sec │ 100% │ ±0.16% │ 101 runs sampled ║ ╟──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────╢ -║ foxify (v0.10.20) │ 2,926,360 ops/sec │ 47.12% │ ±0.22% │ 94 runs sampled ║ +║ foxify (v0.10.20) │ 4,203,055 ops/sec │ 38.25% │ ±0.09% │ 98 runs sampled ║ ╟──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────╢ -║ @hapi/call │ 94,034 ops/sec │ 1.51% │ ±0.6% │ 91 runs sampled ║ +║ @hapi/call │ 88,112 ops/sec │ 0.8% │ ±0.87% │ 99 runs sampled ║ ╟──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────╢ -║ express │ 971,753 ops/sec │ 15.65% │ ±1.59% │ 93 runs sampled ║ +║ express │ 1,582,165 ops/sec │ 14.4% │ ±0.26% │ 98 runs sampled ║ ╟──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────╢ -║ find-my-way │ 3,944,070 ops/sec │ 63.51% │ ±0.32% │ 96 runs sampled ║ +║ find-my-way │ 5,638,877 ops/sec │ 51.31% │ ±0.09% │ 98 runs sampled ║ ╟──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────╢ -║ koa-router │ 1,689,073 ops/sec │ 27.2% │ ±0.37% │ 96 runs sampled ║ +║ koa-router │ 3,704,924 ops/sec │ 33.71% │ ±0.43% │ 100 runs sampled ║ ╟──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────╢ -║ koa-tree-router │ 6,772,736 ops/sec │ 109.06% │ ±1.72% │ 93 runs sampled ║ +║ koa-tree-router │ 9,985,269 ops/sec │ 90.86% │ ±0.12% │ 99 runs sampled ║ ╟──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────┼──────────────────────╢ -║ trek-router │ 4,782,019 ops/sec │ 77.01% │ ±0.48% │ 94 runs sampled ║ +║ trek-router │ 5,871,216 ops/sec │ 53.43% │ ±0.1% │ 99 runs sampled ║ ╚══════════════════════╧══════════════════════╧══════════════════════╧══════════════════════╧══════════════════════╝ -> Fastest is [ 'koa-tree-router' ] +> Fastest is [ '@foxify/router' ] ``` diff --git a/src/Node.ts b/src/Node.ts index b75035e..16a94a1 100644 --- a/src/Node.ts +++ b/src/Node.ts @@ -151,10 +151,10 @@ class Node< return this.children[label]; } - public findChild(path: string): Node | undefined { + public findChild(path: string, index = 0): Node | undefined { const { children } = this; - return children[path[0]] || children[":"] || children["*"]; + return children[path[index]] ?? children[":"] ?? children["*"]; } protected init(prefix: string): this { diff --git a/src/Router.ts b/src/Router.ts index 37337df..d9d3c7d 100644 --- a/src/Router.ts +++ b/src/Router.ts @@ -122,18 +122,18 @@ class Router< path: string, params: Record = {}, ): Partial> { - const originalPath = path; let node: Node | undefined = this.tree; + let position = 0; if (path[0] === "/") { if (path.length === 1) { return node.findHandlers(method); } - path = path.slice(1); + position = 1; } - node = node.findChild(path); + node = node.findChild(path, position); // eslint-disable-next-line no-constant-condition while (true) { @@ -144,15 +144,17 @@ class Router< case NODE.STATIC: { const { prefix, prefixLength, childrenCount } = node; - if (path.length > prefixLength) { - if (childrenCount > 0 && path.slice(0, prefixLength) === prefix) { - path = path.slice(prefixLength); + const length = path.length - position; + + if (length > prefixLength) { + if (childrenCount > 0 && path.indexOf(prefix, position) === position) { + position += prefixLength; - node = node.findChild(path); + node = node.findChild(path, position); continue; } - } else if (prefix === path) { + } else if (length === prefixLength && path.indexOf(prefix, position) === position) { return node.findHandlers(method); } @@ -169,20 +171,20 @@ class Router< case NODE.PARAM: { const { childrenCount, param } = node; - const slashIndex = path.indexOf("/"); + const slashIndex = path.indexOf("/", position); if (slashIndex === -1) { - params[param!] = path; + params[param!] = path.slice(position); return node.findHandlers(method); } if (childrenCount > 0) { - params[param!] = path.slice(0, slashIndex); + params[param!] = path.slice(position, slashIndex); - path = path.slice(slashIndex); + position = slashIndex; - node = node.findChild(path); + node = node.findChild(path, position); continue; } @@ -194,7 +196,7 @@ class Router< case NODE.MATCH_ALL: { const { param, matchAllParamRegExp } = node; - params[param!] = originalPath.match(matchAllParamRegExp!)![1]; + params[param!] = path.match(matchAllParamRegExp!)![1]; return node.findHandlers(method); }