🔄
npm, yarn, pnpmパッケージマネージャをベンチマークしてみた
3行まとめ
- pnpmが速いとのことなのでベンチマークをしてみましたが、yarn v4が最速です。
- yarn v1はキャッシュありのときにとても遅いでが、最新のv4は全項目において最速です。
- Node.jsのdocker imageでデフォルトになっているのはyarn v1なのでご注意ください。
概要
- pnpmがどのくらい速いかを計測しています。
- Next.jsやReactJSを使ったときのベンチマークは掲載されていますが、実際自分のプロジェクトではどの程度かわるのかをベンチマークしてみたいと思います。
- pnpmとは何かを知りたい場合はpnpm概要を参照するのが良いです。
実験環境
個人サービスである銀行コード検索APIのpackage.jsonを使います。
package.jsonの中身は以下のようになっています。
{
"packageManager": "yarn@1.22.15",
"private": true,
"dependencies": {
"@hotwired/turbo-rails": "^7.3.0",
"@popperjs/core": "^2",
"@rails/activestorage": "^7.0",
"@rails/ujs": "^7.0",
"bootstrap": "^5.3",
"bootstrap-icons": "^1.10.5",
"bootswatch": "^5.3",
"esbuild": "^0.18.17",
"jquery": "^3.7"
},
"devDependencies": {
"eslint": "^8.46.0",
"eslint-plugin-react": "^7.33.1",
"npm-check-updates": "^16.10.17"
}
}
測定環境
root@1bf5d376c3a0:/app# node -v
v18.17.0
root@1bf5d376c3a0:/app# npm -v
9.8.1
npmのbenchmark
まずは基本となる npm
を計測します。28秒。
root@1bf5d376c3a0:/app# rm -rf node_modules/ package-lock.json
root@1bf5d376c3a0:/app# time npm install
added 383 packages, and audited 384 packages in 28s
80 packages are looking for funding
run `npm fund` for details
found 0 vulnerabilities
real 0m28.308s
user 0m9.264s
sys 0m5.575s
yarn v1のbenchmark
Node.jsに標準で組み込まれたyarnは、13秒。
root@1bf5d376c3a0:/app# rm -rf node_modules/ package-lock.json
root@1bf5d376c3a0:/app# time yarn install
yarn install v1.22.19
[1/4] Resolving packages...
[2/4] Fetching packages...
[3/4] Linking dependencies...
[4/4] Building fresh packages...
success Saved lockfile.
Done in 13.16s.
real 0m13.362s
user 0m5.338s
sys 0m6.641s/
yarn v4のbenchmark
最新のyarnで計測します。なんと5.8秒!
計測ミスかと思って再度docker runして試しましたが同じ結果です。
root@1bf5d376c3a0:/# mkdir /app
root@1bf5d376c3a0:/# cat > package.json
root@1bf5d376c3a0:/app# yarn set version berry
root@1bf5d376c3a0:/app# yarn -v
4.0.2
root@1bf5d376c3a0:/app# time yarn install
➤ YN0000: · Yarn 4.0.2
➤ YN0000: ┌ Resolution step
➤ YN0085: │ + @hotwired/turbo-rails@npm:7.3.0, @popperjs/core@npm:2.11.8, and 474 more.
➤ YN0000: └ Completed in 3s 435ms
➤ YN0000: ┌ Fetch step
➤ YN0013: │ 455 packages were added to the project (+ 84.83 MiB).
➤ YN0000: └ Completed in 1s 442ms
➤ YN0000: ┌ Link step
➤ YN0000: │ ESM support for PnP uses the experimental loader API and is therefore experimental
➤ YN0007: │ esbuild@npm:0.18.20 must be built because it never has been before or the last one failed
➤ YN0000: └ Completed in 0s 501ms
➤ YN0000: · Done with warnings in 5s 438ms
real 0m5.816s
user 0m7.101s
sys 0m1.844s
pnpmのbenchmark
今回注目のpnpmは18秒。
root@1bf5d376c3a0:/app# time pnpm i
Packages: +347
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Progress: resolved 368, reused 0, downloaded 347, added 347, done
node_modules/.pnpm/esbuild@0.19.2/node_modules/esbuild: Running postinstall script, done in 93ms
dependencies:
+ @popperjs/core 2.11.8
+ @rails/activestorage 7.0.7-2
+ @rails/ujs 7.0.7-2
+ bootstrap-icons 1.10.5
+ bootswatch 5.3.1
+ esbuild 0.19.2
+ jquery 3.7.1
devDependencies:
+ eslint 8.48.0
+ npm-check-updates 16.13.2
Done in 17.9s
real 0m18.139s
user 0m7.202s
sys 0m5.991sx
余談: pnpmのエラー
余談ですが、2回目以降はエラーが出力されて計測できませんでした。
root@1bf5d376c3a0:/app# rm -rf node_modules/ .pnpm-store/
root@1bf5d376c3a0:/app# time pnpm install
Packages: +438
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ERR_PNPM_LINKING_FAILED Error: ENOENT: no such file or directory, copyfile '/app/.pnpm-store/v3/files/cf/83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e' -> '/app/node_modules/.pnpm/resolve@2.0.0-next.4/node_modules/resolve_tmp_124/test/resolver/same_names/foo.js'
Progress: resolved 459, reused 0, downloaded 438, added 197
real 0m20.723s
user 0m9.377s
sys 0m6.953s
追記: package-lock.json
を削除したら成功しました。また、コンテナを立ち上げ直したら成功したりしたのでpnpmが内部で使っているハードリンク関連とDocker for macOSの相性が悪そうです。
キャッシュ無しベンチマーク結果
Package manager | Result (s) |
---|---|
npm | 28.308 |
yarn v1 | 13.362 |
yarn v4 | 5.816 |
pnpm | 18.139 |
考察
- yarn v4がとても速いです。nodeのdocker imageのデフォルトではv1が使われてしまうので明示的にv4を設定する必要があります。
- 今回は初回実行を計測しました。
- pnpmの初回は遅いですが、パッケージがシステム上で共有されること、依存関係の解決が高速なことを考えると2回目の実行はそこそこ速いです。
- 実際の開発では、CIや開発環境でパッケージの差分を追加していくと行った差分での利用が多いと思います。その際には高速に動作するので実運用では利点がありそうです。
次にパッケージマネージャを通して削除、追加する際のベンチマークを計測します。
パッケージの削除、追加の計測
実際の運用時に多いユースケースであるパッケージの追加、削除のときのベンチマークを計測してみます。
bootstrap
パッケージを削除して、追加するケースを想定してベンチマークを取ってみます。
npm
トータル1.5秒。速いです。
root@7d57aaa10c85:/app/a# time npm uninstall bootstrap
removed 1 package, and audited 383 packages in 686ms
80 packages are looking for funding
run `npm fund` for details
found 0 vulnerabilities
real 0m0.863s
user 0m0.772s
sys 0m0.160s
root@7d57aaa10c85:/app/a# time npm install bootstrap
added 1 package, and audited 384 packages in 767ms
81 packages are looking for funding
run `npm fund` for details
found 0 vulnerabilities
real 0m0.943s
user 0m0.858s
sys 0m0.231s
yarn v1
トータル38秒。めちゃ遅いです。
root@7d57aaa10c85:/app/a# time yarn remove bootstrap
yarn remove v1.22.19
[1/2] Removing module bootstrap...
[2/2] Regenerating lockfile and installing missing dependencies...
success Uninstalled packages.
Done in 17.74s.
real 0m18.013s
user 0m5.547s
sys 0m8.845s
root@7d57aaa10c85:/app/a# time yarn add bootstrap
yarn add v1.22.19
warning package-lock.json found. Your project contains lock files generated by tools other than Yarn. It is advised not to mix package managers in order to avoid resolution inconsistencies caused by unsynchronized lock files. To clear this warning, remove package-lock.json.
[1/4] Resolving packages...
[2/4] Fetching packages...
[3/4] Linking dependencies...
[4/4] Building fresh packages...
success Saved lockfile.
success Saved 1 new dependency.
info Direct dependencies
└─ bootstrap@5.3.1
info All dependencies
└─ bootstrap@5.3.1
Done in 20.55s.
real 0m20.919s
user 0m5.861s
sys 0m9.193s
yarn v4
addとremoveがそれぞれ1秒以内に終わります。速い!
root@0c8caa909f49:/app# time yarn remove bootstrap
➤ YN0000: · Yarn 4.0.2
➤ YN0000: ┌ Resolution step
➤ YN0085: │ - bootstrap@npm:5.3.2
➤ YN0000: └ Completed
➤ YN0000: ┌ Fetch step
➤ YN0000: └ Completed
➤ YN0000: ┌ Link step
➤ YN0000: │ ESM support for PnP uses the experimental loader API and is therefore experimental
➤ YN0000: └ Completed
➤ YN0000: · Done with warnings in 0s 362ms
real 0m0.848s
user 0m0.778s
sys 0m0.375s
root@0c8caa909f49:/app# time yarn add bootstrap
➤ YN0000: · Yarn 4.0.2
➤ YN0000: ┌ Resolution step
➤ YN0085: │ + bootstrap@npm:5.3.2
➤ YN0000: └ Completed
➤ YN0000: ┌ Fetch step
➤ YN0000: └ Completed
➤ YN0000: ┌ Link step
➤ YN0000: │ ESM support for PnP uses the experimental loader API and is therefore experimental
➤ YN0000: └ Completed
➤ YN0000: · Done with warnings in 0s 330ms
real 0m0.791s
user 0m0.820s
sys 0m0.171s
pnpm
トータル4.7秒。速いです。
root@7d57aaa10c85:/app# time pnpm remove bootstrap
Packages: -1
-
Progress: resolved 367, reused 346, downloaded 0, added 0, done
dependencies:
- bootstrap 5.3.1
Done in 2.2s
real 0m2.311s
user 0m2.041s
sys 0m0.906s
root@7d57aaa10c85:/app# time pnpm add bootstrap
Packages: +1
+
Progress: resolved 368, reused 347, downloaded 0, added 0, done
dependencies:
+ bootstrap 5.3.1
Done in 2.1s
real 0m2.239s
user 0m2.205s
sys 0m0.879s
remove/addのベンチマーク結果
Package manager | Remove Result (s) | Add Result (s) |
---|---|---|
npm | 0.863 | 0.943 |
yarn v1 | 18.013 | 20.919 |
yarn v4 | 0.8480 | 0.791 |
pnpm | 2.311 | 2.239 |
考察
pnpmが公表しているベンチマーク結果とは違いましたやはり手元でやってみるのは意味があります。
jsbundling-rails
はyarnに依存しているので要注意です。
Discussion
npm よりも yarn を使うべきですか? - Quora
プロジェクトでyarnとnpmのどちらを使うかの判断基準は何ですか? - Quora
数年後の先を見通した技術選定って、結構、難しいですよね。
ですね。
JavaScriptのパッケージマネージャの情勢がすぐ変わるので難しいです。幸い、パッケージマネージャは乗り換えが楽なのが救いです。
手元では、jsbundle-railsがyarnに依存しているのでyarnを使う必要があります。
yarn --watch
を内部で使っているようです。これはnpm標準で代用できないので。この記事を初めて公開したときのYarnの最新版はv3だったのですが、v1で検証してるのは何か理由があるのでしょうか👀
nodeに付属しているyarnが1系だからそのまま使っちゃってました。
v3でも計測してみます。と思いましたが、v4が出ているのでv4で計測します。→しました。記事を更新しました!
ありがとうございます!!