1567 文字
8 分

npm, yarn, pnpmパッケージマネージャをベンチマークしてみた

3行まとめ#

  • pnpm が速いとのことなのでベンチマークをしてみましたが、Yarn v4 が最速です。
  • Yarn v1 はキャッシュありのときにとても遅いでが、最新の v4 は全項目において最速です。
  • Node.js の Docker image でデフォルトになっているのは Yarn v1 なのでご注意ください。

概要#

  • pnpm がどのくらい速いかを計測しています。
  • Next.js や React を使ったときのベンチマークは掲載されていますが、実際自分のプロジェクトではどの程度かわるのかをベンチマークしてみたいと思います。
  • pnpm とは何かを知りたい場合は pnpm概要を参照するのが良いです。

実験環境#

個人サービスである 銀行コード検索APIのpackage.jsonを使います。

package.json の中身は以下のようになっています。

{
"packageManager": "[email protected]",
"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/[email protected]/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/[email protected]/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 managerResult (s)
npm28.308
yarn v113.362
yarn v45.816
pnpm18.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
info All dependencies
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 managerRemove Result (s)Add Result (s)
npm0.8630.943
yarn v118.01320.919
yarn v40.84800.791
pnpm2.3112.239

考察#

pnpmが公表しているベンチマーク結果とは違いましたやはり手元でやってみるのは意味があります。

Alt text

jsbundling-rails は Yarn に依存しているので要注意です。

参考資料#

npm, yarn, pnpmパッケージマネージャをベンチマークしてみた
https://blog.teraren.com/posts/2023-08-30-pnpm/
作者
Yuki Matsukura
公開日
2023-08-30
ライセンス
CC BY-NC-SA 4.0
この記事が役に立ったら
GitHub Sponsorsで応援できます

コメント