webpack 4のビルド時間の計測と速度改善方法


## ビルド時間の検証
Webpack 4のビルドの速度検証してみました。
行なった検証内容は以下の通りです。

* [Nodeのバージョンの違いによる速度検証](#Nodeのバージョンの違いによる速度検証)
* [マルチコアを使ったビルド時の速度検証](#マルチコアを使ったビルド時の速度検証)
* [キャッシュの適用によるビルド時間の検証](#キャッシュの適用によるビルド時間の検証)

|||
|:--|:--|
|webpackのバージョン | 4.40.2|
|Nodeのバージョン| >= v6.9.0|

## Nodeのバージョンの違いによる速度検証

**計測結果**

|Nodeのバージョン|ビルドタイム|
|:--|:--|
|v6|5826ms|
|v7|5789ms|
|v8|3491ms|
|v9|3662ms|
|v10|3301ms|
|v11|3361ms|
|v12|3290ms|

**Conclusion**
デグレードがあるバージョンが存在するものの、バージョンが上がるにつれて
ビルド時間が縮小されていることが分かります。なお、コンパイル後のサイズには変化がありませんでした。

## マルチコアを使ったビルド時の速度検証

Webpackの公式ページに掲載されているファイル圧縮用のプラグイン **[uglifyjs-webpack-plugin](https://www.npmjs.com/package/uglifyjs-webpack-plugin/v/1.3.0)** には複数のCPUコアを使ったビルドができる機能がサポートされいます。このPluginは **v1.0.0** がデフォルトでwebpackに含まれる予定だそうですが、現在はnpm経由で手動でインストールする必要があります。

```bash
npm install --save-dev uglifyjs-webpack-plugin 
```

また、Pluginのリファレンスには、以下のアノテーションがあります。
>Parallelization can speedup your build significantly and is therefore highly recommended

ビルド速度を上げるために強く推奨されていますね。

ビルド時にマルチコアを適用させるためには、**parallel** プロパティを有効にするか、使用するコア数を指定する必要があります。Pluginの記述は以下の通りです。

```js
optimization: {
  // NOTE: Use multi-process parallel running to improve the build speed
  //       端末のコア数が多い場合は parallel: 4 などと設定するとビルド時間がもっと短縮される
  minimizer: [
    new UglifyJsPlugin({
      parallel: true, // またはコア数 4 などを指定
      cache: true
    })
  ],
} 
```

|||
|:--|:--|
|検証時のNodeのバージョン|v12|

**計測結果**

|CPUコア数|ビルドタイム|
|:--|:--|
|シングルコア| 66395ms |
|デュアルコア|54614ms|
|クアッドコア|51362ms <br>※検証端末がサポートしていないので正確ではありません。|

**Conclusion**
マルチコアに設定すると、ビルド時間が短縮されることがわかります。とくに規模が大きいプロジェクトをビルドする時には数秒短縮できることが分かりました。また、検証端末はクアッドコアではありませんが、コア数を4に設定して計測してみました。結果としてはデュアルコアより若干スピードアップしているようですが、検証端末はクアッドコアをサポートしていないため、デュアルコアで計測していると思われます。おそらく今回のデュアルコアとクアッドコアのビルド時間の差は誤差の範囲ですね。

webpack側では以下のようなコードでコア数の設定が使われており、OSのコア数を見て使うコア数を決めているようです。

```js
os.cpus().length - 1
```


## キャッシュの適用によるビルド時間の検証

|||
|:--|:--|
|検証時のNodeのバージョン|v12|

uglifyプラグインには **cache** プロパティの設定があります(default : false)。
これを有効にすると、 **node_modules/.cache/uglifyjs-webpack-plugin** ここにキャッシュが作成され、次回からこのキャッシュが参照されるようになります。

```js
  new UglifyJsPlugin({
    cache: true,
  })
```
**計測結果**

|||
|:--|:--|
|キャッシュ適用前|66395ms|
|キャッシュ適用後|15884ms|

キャッシュ適用後は劇的にビルド時間が短縮されました。
さらに、マルチコアに対応させてビルド時間を計測してみました。

```js
  new UglifyJsPlugin({
    parallel: true,
    cache: true
  })
```

|||
|:--|:--|
|キャッシュ適用 + マルチコア|12947ms|

#### Conclusion
キャッシュに溜め込む必要があるため2回目以降のビルド時間が計測対象となりますが、キャッシュを適用すると、変更がないファイルに対しては2回目以降のビルド時間が劇的に短縮されることが分かります。ファイルに変更があると通常のビルド時間になります。なので複数のプロジェクトファイルをビルドする時は変更がないファイルが出てくると思うので、そういった場合には有効そうです。

## Overall
Nodeのバージョンの違いによってビルド時間が速くなることがわかりました。とくにバージョン **v8** 以降はかなり速度改善されているので、Nodeのバージョンを上げるだけでも効果があります。また、キャッシュと有効にすると、2回目以降の変更がないファイルに対してはビルド時間が短縮されます。そのため、複数プロジェクトをビルドする必要がある場合は変更を加ないプロジェクトファイルが出てくるため、結果として全体のビルド時間が縮小されます。ただ、そういうケースはビルド対象のファイルを絞ってコンパイルする方が賢明かもしれませんが。。。
まぁでもキャッシュを適用させてマルチコアにも対応した全部のせ状態にするとさらにビルド時間が短縮されるため、JavaScriptのコンパイル圧縮に **UglifyJsPlugin** を使う場合は積極的に採用していくと良いと思います。


最後に、最終的なwebpack.config.jsの例を載せておきます。

```js
const UglifyJsPlugin = require("uglifyjs-webpack-plugin");

module.exports =  {
    mode: 'production',
    entry: {
      pageOne: './src/pageOne/index.js',
      pageTwo: './src/pageTwo/index.js',
      pageThree: './src/pageThree/index.js'
    },
    output: {
      filename: '[name].js',
      path: __dirname + '/dist'
    },
    module: {
      rules: [{ 
          test: /\.js$/,
          exclude: /node_modules/,
          use: {
              loader: 'babel-loader',
              options: {
                presets: ['@babel/preset-env']
              }
            }
      }]
    },
    optimization: {
      // NOTE: Use multi-process parallel running to improve the build speed
      //       端末のコア数が多い場合は parallel: 4 などと設定するとビルド時間がもっと短縮される
      minimizer: [new UglifyJsPlugin({
        parallel: true,
        cache: true
      })],
    }
  };
}
```