hidden class slots) and inefficient representations (e.g. JavaScript doesn't have these features and also has other drawbacks such as JIT overhead (e.g. Go also has value semantics and can embed one object directly in another so it comes "for free" without another allocation. several boolean flags only take one byte each. All object fields have types and fields are packed tightly together so e.g. string→TS→JS→string, then string→JS→older JS→string, then string→JS→minified JS→string) which uses more memory and slows things down.Īnother benefit of Go is that it can store things compactly in memory, which enables it to use less memory and fit more in the CPU cache. They may also convert between data representations to glue multiple libraries together (e.g. Other bundlers do these steps in separate passes instead of interleaving them. This maximizes reuse of AST data while it's still hot in the CPU cache.
![webpack documentation github webpack documentation github](https://discourse.roots.io/uploads/default/original/2X/a/a45388f7d4c5394dffe935f84c6b597e5344891f.png)
And the TypeScript parser appears to still run the type checker even when type checking is disabled.
WEBPACK DOCUMENTATION GITHUB CODE
Their code makes pretty heavy use of megamorphic object shapes and unnecessary dynamic property accesses (both well-known JavaScript speed bumps). But it was built to serve the goals of the TypeScript compiler team and they do not have performance as a top priority. The drawback is of course that it's a lot of work.įor example, many bundlers use the official TypeScript compiler as a parser.
![webpack documentation github webpack documentation github](https://user-images.githubusercontent.com/1495211/28369638-80888592-6c98-11e7-9c58-d6bbaf6b0fbe.png)
You can have performance in mind from the beginning, you can make sure everything uses consistent data structures to avoid expensive conversions, and you can make wide architectural changes whenever necessary. There are a lot of performance benefits with writing everything yourself instead of using 3rd-party libraries. Most modern computers have many cores so parallelism is a big win.Įverything in esbuild is written from scratch. Since all threads share memory, work can easily be shared when bundling different entry points that import the same JavaScript libraries.
WEBPACK DOCUMENTATION GITHUB SERIAL
Parsing and code generation are most of the work and are fully parallelizable (linking is an inherently serial task for the most part). There are roughly three phases: parsing, linking, and code generation.
![webpack documentation github webpack documentation github](https://image.slidesharecdn.com/webpack-170527125352/95/webpack-16-638.jpg)
The algorithms inside esbuild are carefully designed to fully saturate all available CPU cores when possible. This seems to cut the amount of parallelism that's possible with JavaScript worker threads in half according to my testing, presumably since half of your CPU cores are busy collecting garbage for the other half. Both Go and JavaScript have parallel garbage collectors, but Go's heap is shared between all threads while JavaScript has a separate heap per JavaScript thread. Go has shared memory between threads while JavaScript has to serialize data between threads. In addition, Go is designed from the core for parallelism while JavaScript is not. By the time node has finished parsing your bundler's code, esbuild might have already exited and your bundler hasn't even started bundling yet. While esbuild is busy parsing your JavaScript, node is busy parsing your bundler's JavaScript. Every time you run your bundler, the JavaScript VM is seeing your bundler's code for the first time without any optimization hints. Most other bundlers are written in JavaScript, but a command-line application is a worst-case performance situation for a JIT-compiled language. It's written in Go and compiles to native code. You can also ask questions on the GitHub issue tracker. This is a collection of common questions about esbuild.