Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

perf(linker): Fixes brute force chunk cycle detection #3069

Merged
merged 4 commits into from
Apr 22, 2023

Conversation

ruiconti
Copy link
Contributor

@ruiconti ruiconti commented Apr 17, 2023

Fixes #3068

Approach

Given that the behavior is concerned only on detecting a cycle rather than identifying them, the simplest possible revision would be to improve current approach's shortcomings. Practically, this would mean

  1. Memoize: store and use the results of validating a given node
  2. Reduce the search space: skip nodes that have already been visited

I used an approach borrowed from topological sorting, in which nodes are marked with a state (not-visited, visiting and visited), and if they've already been visited, we skip them. Furthermore, if we find a cycle, we halt. That gives us a linear execution time, rather than exponential.

Results and reproducibility

It's clear that the impact of this would only be seen on some areas of the input space. Particularly, past a given graph complexity. Consuming the change from this fork, the vendor build which was taking up to ~400s, dropped to 4s, a 100x (anecdotal) improvement. The app build's incremental times dropped from ~150s to ~30s, a 5x improvement.

Since this is a plain algorithmic revision, I believe that the point could be proven with a benchmarked test, and didn't see the need to generate a sufficiently complex npm dependency graph and run a full esbuild build on it.

Code Sandbox: https://codesandbox.io/p/sandbox/blue-http-750ebf?file=%2Flinker_test.go&selection=%5B%7B%22endColumn%22%3A6%2C%22endLineNumber%22%3A10%2C%22startColumn%22%3A6%2C%22startLineNumber%22%3A10%7D%5D

PASS
BenchmarkLegacy100Sparse          100000             12931 ns/op             896 B/op          1 allocs/op
BenchmarkLegacy200Sparse             100          15657528 ns/op            1818 B/op          3 allocs/op
BenchmarkLegacy300Sparse               1        19536221791 ns/op          28712 B/op        446 allocs/op
BenchmarkRevision50Sparse         100000             12944 ns/op            2542 B/op          7 allocs/op
BenchmarkRevision100Sparse         50000             26291 ns/op            5279 B/op         11 allocs/op
BenchmarkRevision200Sparse         30000             55162 ns/op           10769 B/op         18 allocs/op
BenchmarkRevision300Sparse         20000             98047 ns/op           20824 B/op         23 allocs/op
BenchmarkRevision1000Sparse         3000            454412 ns/op           88313 B/op         67 allocs/op

@evanw
Copy link
Owner

evanw commented Apr 22, 2023

Thanks for the improvement and for the detailed report.

However, note that encountering this problem likely means that esbuild is a poorly-suited tool for your use case. It's not good to have too many chunks at run-time and esbuild doesn't currently have the ability to control the number of chunks (as it's code splitting feature is still in a WIP state). So you probably want to look for another tool even after this improvement has landed.

@evanw evanw merged commit 9092a1b into evanw:main Apr 22, 2023
@MLoughry
Copy link

@evanw We currently use esbuild for dev innerloop builds, where the output is "good enough" and the build speed is much faster than our webpack dev builds. But we will certainly keep that in mind if/when we investigate bundlers aside from webpack for production builds.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Naive chunks' cycle detect assertion
3 participants