kostumブログ

勉強したことやノート代わりのアウトプットに使っています。

e18eを知ってみた

おはこんにちばんわ。

今回は、「e18e」というプロジェクトについて興味を持ったので、深掘りしていきたいと思います!

READMEやdocs配下のmarkdownを日本語に訳しながら見ていきたいと思います。

github.com

e18eとは?

README.md

e18e(Ecosystem Performance)プロジェクトとは、JavaScriptのパフォーマンス向上に情熱のある団体や個人を引き合わせるプロジェクトです。

この場では、依存関係からパフォーマンスの最適化まで、多くの継続的な取り組みが行われています。

我々の目的は、エコシステムを通してパフォーマンスの重要性を広めることで、貢献、アイディア、知識の場を発展させることです。 願わくば、このコラボレーションによって依存関係の選択などが及ぼす影響を見える化したいです。

現在、3つの観点にを注視しています:

  • cleanup: エコシステムを用いている依存関係やよく知られているツールやライブラリの最適化
  • speedup: 私たちの多くが依存している一部のエコシステムの速度向上
  • levelup: 私たちが普段使っている既存のツールやライブラリに代わる、モダンで軽量なものの文書化と、その提供。

私たちが普段使っている既存のツールやライブラリに代わる、モダンで軽量なものを文書化し、提供する。

Contributing

詳しくは、コントリビューションガイドをご覧ください。

Credits

  • cleanups ドキュメントは、James Garbutt さんの「ecosystem-cleanup」の一部を基にしています。
  • ドキュメントサイトは、Vite docsを使っています。
  • このリポジトリは、Anthony Fuさんのおすすめです。

cleanup

JavaScriptのエコシステムは、良くも悪くも巨大に成長してきました。我々は非常に多くのパッケージを使うことができ、自分たちが望む選択をできますが、長い間、膨大な量の負の遺産を作ってしました。

このほとんどが、過剰だったり、膨れ上がったり、アクティブなメンテナンスができなくなったパッケージで形作られています。

現在、パッケージ最適化で勢いのある例として、 @43081j's ecosystem-cleanup があります。 このプロジェクトは、多くの様々なオープンソースプロジェクトの上流工程に貢献し、促進することを目的としています。

このプロジェクトの目的は:

  • 現在のパッケージの最新化
  • 余剰パッケージからの移行
  • 動いていないパッケージからの移行
  • 軽量もしくは高速な代替品(パッケージ)への移行

コントリビュートのためのガイド

このガイドは、それらのエコシステムを発見し、コントリビュートの発展の手助けを行う場所です。

上流パッケージにおいて、いくつかの共通の発見テクニックと解決すべき課題を説明します。

発見方法

我々は、以下の判断基準に合うパッケージを探しています。

  • 肥大している
  • もはやメンテナンスされていない
  • 古い
  • 遅い
  • 余剰

まずは、改善したパッケージを選ぶことです。例えば、viteを選んだことにしましょう。

最初のステップは、このパッケージの現在の状態をdiscover(発見)することです。 npmgraphpkg-sizeを使うことができます。 この場合では、viteの依存関係のグラフとサイズを可視化できます。

これによって、複雑な依存ツリーや全体のインストールサイズ(インストール時に落としているデータの量)を知ることができます。

Rollup bundle

例では、viteは(執筆時点で)とても少ない依存関係でインストールサイズを減らすことができるようです。

これは、vitenode_modulesから引っ張ってくるというよりは、依存関係の多くをバンドルしているからです。

このため、rollupバンドラの分析に踏み込む必要があります。

viteのバンドル内を可視化生成できるrollup-plugin-visualizerというプラグインを使います。

この成果物から、インポートされているバンドルやツリー内の大きな依存関係を素早く把握することができます。

rollup-plugin-visualizer stats.html
rollup-plugin-visualizer stats.html

依存度の深さに注目する

依存関係のグラフでは、非常に巨大な依存関係を持つサブツリーに着目すると簡単に勝つことができる(減らす対象を把握することができる)。

このことは、通常なら正しいですが、(古いバージョンのnodeなど)いくつかの深い依存関係には使い所があります。依存関係のリストからツリーを削除する前に、これを考慮に入れるのを忘れないようにしてください。

改善

今、依存関係のグラフ、パッケージサイズ、バンドルサイズを可視化したので、改善について考えることができます。

(その可視化したものの中で)何を探すべきか:

  • それ自身が外部のサブツリーに何も依存していない、より深い依存関係の巨大なサブツリーを持つ依存関係。
  • (複数のバージョンや同じ目的を達成する依存関係を持つような)重複した依存関係
  • 非常に巨大な(インストールサイズの)依存関係

viteの例に戻ると、(この記事を書いているとき、)viteにバンドルされているfast-grabglabの両方がバンドルされているrollupを見ることができます。 (バンドルされているのは確認できませんでした。。。)

これは、同じことをする2つのパッケージをバンドルしていることを意味しています。パフォーマンスを改善できる可能性があります!

cleanup issueを立てる

ここでのポイントは、いくつかの潜在的なパフォーマンス改善を特定できたら、このリポジトリにissueをオープンすることです。

たとえ自分で取り組むとしても、issueを立てることで視認性が向上し、仕事の重複を防ぎ、あなたを助けることができます。

もしくは、自分で取り組む時間がなければ、あなたの気づきを誰かにフォローしてもらうことも可能です。

DevMinerさんのpackage-size-calculatorを使ったパッケージレポートもissueに添付して、パッケージサイズとあなたが提案している変更の概要を説明してください。

修正

パフォーマンスの問題があるいくつかの依存関係を特定しました。

特定した改善の種類によると、修正や変更を適用するには、いくつか異なるアプローチがあります。

巨大なサブツリー

これらの依存関係では、まず最初に代替案ではなくそれが必要な理由があるかどうかを調べるべきです。

一般的なチェック観点:

  • この依存関係がサポートしているnodeが、古いnodeバージョンを指定している(そして、代替品はサポートしていない)。
  • 知られている代替品がない(代替品を作る良い機会だ!)。
  • サブツリーにある、どの深い依存関係も必要である(多くの場合、ツリーは深いかもしれないが、モジュールを共有するなどの正当な理由があります)。

もし、それが切り替えられるなら、代替品を見つけるべきです。そのための良いリソースは、

機能的に重複している

多くの場合、同じ機能を実現するために、別々のバージョンや全く別のソリューションを使用している依存関係を見つけることがあります。

別々のバージョンの場合、遅れている方を最新バージョンにアップデートするために、上流に貢献して欲しいです(どちらも最新でない場合は両方)。

別々のソリューションの場合、一方のソリューションが他のものより優れているか確認し、そうであれば、一方のソリューションへの移行を考慮すべきです。

viteの例で言うと、何がfast-globを使うか、何がglobを使うかと言うことです。そして、2つのライブラリと代替案を比較し、それらの依存関係が同じソリューションを使えるかどうか、あるいはより新しく優れたソリューションを使えるかどうかを調べます。

巨大な依存関係

巨大な依存関係を見つけた場合、大抵は必要な機能よりもはるかに多くの(有用な)機能が含まれているものです。

ここでの修正は、以下のいずれかである場合が多いです:

  • より粒度の粗いモジュールに分割するように上流に貢献する
  • よりスリムで、より焦点を絞った依存関係に移行する

このような状況では、より軽量な(願わくばより高速な)代替品を探す価値があります。

Speedup

誰にとっても、エコシステムを最適化する上で重要なことは、スピードです。コアパッケージ、ツールやそのほかのスピードは、常に最適化でき、コミュニティ全体に利益をもたらすでしょう。

いくつかの団体や個人の方達が、以下の場で動いています。以下に幾つかをリスト化しておきます:

Linting

いくつかのパフォーマンス最適化はLinterを介して検出することができます。

ESLint

ESLintを使っていれば、e18eの取り組みの原則に強く合致するプラグインを使うことができます:

Plugin 説明
eslint-plugin-depend 冗長なパッケージを検出し、よりパフォーマンスの良い代わりのパッケージを提案する
eslint-plugin-barrel-files バレル(他のファイルからたくさんのものを再エクスポートしただけのファイル)を検出する

Biome

Biomeはe18eに合致するいくつかのルールをサポートしています。

Coding tips

アプリケーションのパフォーマンスにおいて、記載するコードは重要な役割を担います。よくある落とし穴と改善方法について、いくつかのパターンを以下に紹介します。

パフォーマンスを変更する際には、常にコードのプロファイリングを行うことを忘れないでください!

ホットコードパスの作成は避ける

現時点では、ほとんどのJavaScriptエンジンはgenerator関数(イテレータを生成する関数)呼び出しを最適化していないため、広範囲に使用すると、パフォーマンスが大きく低下します。

非同期イテレータやプレーンな配列を使用することをお勧めします。

補足

developer.mozilla.org

qiita.com

qiita.com

配列のチェーンメソッドを避ける

map, filter, reduceなどのような配列のチェーンメソッドをつなげると、多くの中間配列が生成・廃棄されることになり、ガベージコレクタの処理が増えてしまいます。また、連鎖のたびに余計な反復が発生し、必要以上の回数になることもあります。

forループ、for...inループ、for...ofループ、またはシングルチェーンメソッドのみを使用することで、上記の注意点を防ぐことができます。

補足

developer.mozilla.org

Levelup

特に最近のランタイムがもたらす機能によって、多くのパッケージはよりスリムに、より速く、より焦点を絞ることができるようになりました。

webpackのようなカスタマイズ性を必要としない場合、軽量なものの代替手段としてesbuildが導入されたように、これらのパッケージは高速かつ小規模であるためにここにあります。

この取り組みに大きく貢献しているプロジェクトもあります:

LevelupとCleanupプロジェクトの取り組みは、しばしば交差しています。これらのパッケージは、インストールサイズを大幅に削減すると同時に、すばらしいパフォーマンス向上を与えてくれるからです。

参考

e18e.dev

Efforts

   Description 
JS ecosystem cleanup Efforts to provide a cleaner dependency tree in the JS ecosystem      
Tinylibs                             A place for tiny and minimal libraries                                
unjs                                    Purpose-built, high-quality JavaScript utilities, libraries, and tools
es-tooling                        A collection of tooling for the JS ecosystem                          

Tools

                                                                                      Description                                                                                                                                    
eslint-plugin-depend            ESLint plugin to suggest dependencies alternatives                                                                                             
eslint-plugin-barrel-files ESLint plugin to avoid barrel files                                                                                                            
Biome's noBarrelFile              Biome plugin to avoid barrel files                                                                                                             
Biome's noReExportAll           Biome plugin to avoid re-export all                                                                                                            
unplugin-purge-polyfills     Unplugin to replace package imports with native code                                                                                           
Package Size Calculator   CLI to calculate the size of a package after removing/replacing dependencies, or calculating the difference of two versions of the same package

Learning