Skip to content

Commit

Permalink
docs(guides): add build-performance page (#1506)
Browse files Browse the repository at this point in the history
This adds a solid baseline for how to start improving build performance
with specific pointers for development and production. It can be built on
in other PRs but probably doesn't need to be synchronized with the earlier
guides (as it's a lot of one off changes that depend on your use case).
  • Loading branch information
sokra authored and skipjack committed Aug 11, 2017
1 parent 13661b9 commit 2b9b3e4
Showing 1 changed file with 199 additions and 0 deletions.
199 changes: 199 additions & 0 deletions content/guides/build-performance.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
---
title: Build Performance
sort: 17
contributors:
- sokra
---

This guide contains some useful tips for improving build/compilation performance.

---

## General

The following best practices should help whether or not you are in [development](/guides/development) or building for [production](/guides/production).


### Stay Up to Date

Use the latest webpack version. We are always making performance improvements. The latest stable version of webpack is:

[![latest webpack version](https://img.shields.io/npm/v/webpack.svg?label=webpack&style=flat-square&maxAge=3600)](https://github.com/webpack/webpack/releases)

Staying up to date with __Node.js__ can also help with performance. On top of this, keeping your package manager (e.g. `npm` or `yarn`) up to date can also help. Newer versions create more efficient module trees and increase resolving speed.


### Loaders

Apply loaders to the minimal number of modules necessary. Instead of:

``` js
{
test: /\.js$/,
loader: "babel-loader"
}
```

Use the `include` field to only apply the loader modules that actually need to be transformed by it:

``` js
{
test: /\.js$/,
include: path.resolve(__dirname, "src"),
loader: "babel-loader"
}
```


### Bootstrap

Each additional loader/plugin has a bootup time. Try to use a few different tools are possible.


### Resolving

The following steps can increase the speed of resolving:

- Minimize the number of items in `resolve.modules`, `resolve.extensions`, `resolve.mainFiles`, `resolve.descriptionFiles` as they increase the number of filesystem calls.
- Set `resolve.symlinks: false` if you don't use symlinks (e.g. `npm link` or `yarn link`).
- Set `resolve.cacheWithContext: false` if you use custom resolving plugins, that are not context specific.


### Dlls

Use the `DllPlugin` to move code that is changed less often into a separate compilation. This will improve the application's compilation speed, although it does increase complexitity of the build process.


### Smaller = Faster

Decrease the total size of the compilation to increase build performance. Try to keep chunks small.

- Use less/smaller libraries.
- Use the `CommonsChunksPlugin` in Multi-Page Applications.
- Use the `CommonsChunksPlugin` in `async` mode in Multi-Page Applications.
- Remove unused code.
- Only compile the part of the code you are currenly developing on.


### Worker Pool

The `thread-loader` can be used to offload expensive loaders to a worker pool.

W> Don't use too many workers as there is a boot overhead for the Node.js runtime and the loader. Minimize the module transfers between worker and main process. IPC is expensive.

This comment has been minimized.

Copy link
@SEAPUNK

SEAPUNK Aug 11, 2017

Contributor

Module transfers? What's that, and how does that happen?

This comment has been minimized.

Copy link
@skipjack

skipjack Aug 11, 2017

Collaborator

Module transfers? What's that, and how does that happen?

@SEAPUNK I think it means when modules are passed between the worker and main process. We should probably spell out what IPC means though (inter-process communication I think). @sokra could probably explain more but you may also be to find out more by looking into the thread-loader:

https://github.com/webpack-contrib/thread-loader

This comment has been minimized.

Copy link
@SEAPUNK

SEAPUNK Aug 11, 2017

Contributor

We should probably spell out what IPC means though

Generally, I'm more interested in what all the webpack jargon means, rather than stuff I could easily Google in 10 seconds and get 100 articles on. While webpack has made significant improvements to its documentation (thank god for this new website), I still find it fairly suffering for advanced use cases, especially with nature's greatest mysteries, like CommonsChunkPlugin which I can't really make heads or tails of, even when trying to dive in the thoroughly confusing source code.

This comment has been minimized.

Copy link
@skipjack

skipjack Aug 11, 2017

Collaborator

Generally, I'm more interested in what all the webpack jargon means, rather than stuff I could easily Google in 10 seconds and get 100 articles on.

Yeah I see your point but it's good for us to quickly clarify or link out to those things as well.

While webpack has made significant improvements to its documentation (thank god for this new website), I still find it fairly suffering for advanced use cases, especially with nature's greatest mysteries, like CommonsChunkPlugin which I can't really make heads or tails of, even when trying to dive in the thoroughly confusing source code.

I totally understand however there's only so much we can do at once. Initially our focus was getting the site up and porting/rewriting existing documentation. After that it was filling in the backlog of missing documentation -- which we've made a lot of progress on. Now we're starting projects like #1258 and #1386 to clean up, synchronize, dedupe and improve the existing documentation so it's more intuitive and flows better.

That said, we encourage and are happy to take contributions. For example, if you wanted clarify that line above:

Minimize the module transfers between worker and main process. IPC is expensive.

to something like:

Minimize the the passing of modules between the worker thread and main process. Inter process communication (IPC) is expensive.

We'd happily accept a PR.

This comment has been minimized.

Copy link
@SEAPUNK

SEAPUNK Aug 11, 2017

Contributor

Sounds good. I'll see if I can take some time to research more into the topic(s), and possibly open up a PR or two along the way.



### Persistent cache

Enable persistent caching with the `cache-loader`. Clear cache directory on `"postinstall"` in `package.json`.


### Custom plugins/loaders

Profile them to not intruduce a performance problem here.

---


## Development

The following steps are especially useful in _development_.


### Incremental Builds

Use webpack's watch mode. Don't use other tools to watch your files and invoke webpack. The built in watch mode will keep track of timestamps and passes this information to the compilation for cache invalidation.

In some setups watching falls back to polling mode. With many watched files this can cause a lot of CPU load. In these cases you can increase the polling interval with `watchOptions.poll`.


### Compile in Memory

The following utilities improve performance by compiling and serving assets in memory rather than writing to disk:

- `webpack-dev-server`
- `webpack-hot-middleware`
- `webpack-dev-middleware`


### Devtool

Be aware of the performance differences of the different `devtool` settings.

- `"eval"` has the best performance, but doesn't assist you for transpilied code.
- The `cheap-source-map` variants are more performant, if you can live with the slightly worse mapping quality.
- Use a `eval-source-map` variant for incremental builds.

=> In most cases `eval-cheap-module-source-map` is the best option.


### Avoid Production Specific Tooling

Certain utilities, plugins and loader only make sense when building for production. For example, it usually doesn't make sense to minify and mangle your code with the `UglifyJsPlugin` while in development. These tools should typically be excluded in development:

- `UglifyJsPlugin`
- `ExtractTextPlugin`
- `[hash]`/`[chunkhash]`
- `AggressiveSplittingPlugin`
- `AggressiveMergingPlugin`
- `ModuleConcatenationPlugin`


### Minimal Entry Chunk

webpack only emits updated chunks to the filesystem. For some configuration options (HMR, `[name]`/`[chunkhash]` in `output.chunkFilename`, `[hash]`) the entry chunk is invalidated in addition to the changed chunks.

Make sure the entry chunk is cheap to emit by keeping it small. The following code block extracts a chunk containing only the runtime with _all other chunks as children_:

``` js
new CommonsChunkPlugin({
name: "manifest",
minChunks: Infinity
})
```

---


## Production

The following steps are especially useful in _production_.

W> __Don't sacrifice the quality of your application for small performance gains!__ Keep in mind that optimization quality is in most cases more important than build performance.


### Multiple Compilations

When using multiple compilations the following tools can help:

- [`parallel-webpack`](https://github.com/trivago/parallel-webpack): It allows to do compilation in a worker pool.
- `cache-loader`: The cache can be shared between multiple compilations.


### Source Maps

Source maps are really expensive. Do you really need them?

---


## Specific Tooling Issues

The following tools have certain problems that can degrade build performance.


### Babel

- Minimize the number of preset/plugins


### Typescript

- Use the `fork-ts-checker-webpack-plugin` for type checking in a separate process.
- Configure loaders to skip typechecking.
- Use the `ts-loader` in `happyPackMode: true` / `transpileOnly: true`.


### Sass

- `node-sass` has a bug which blocks threads from the Node.js threadpool. When using it with the `thread-loader` set `workerParallelJobs: 2`.

0 comments on commit 2b9b3e4

Please sign in to comment.