00:06
3月24日、金曜日ですね。 時刻は朝9時11分になりました。
ちょっと今日ですね、まだ風邪の調子が直ってなくてですね、
変な声かもしれないんですけど、 声を教えただければ幸いです。
はい、おはようございます。 耳のkeethこと桑原です。
では本日も朝活を始めていきたいと思います。
本日もですね、タイトルになります。
スピーディングアップ ザ・ジャバスクリプト・エゴシステム
今回は、one library at a timeというところですね、
の記事を読んでいこうと思っています。
この記事はですね、シリーズものなんですけど、
シリーズが1、2、3、4と続いていて、
僕は逆流をしてしまして、
シリーズ3ですね、ところからこの記事を読んでしまったので、
そこから2、1と下がって、読んでいる途中でシリーズ4、新しいの出たので、
最後に4に飛ぼうという感じで、 ちょっと変則的な動きですけど、
ご了承いただければなと思います。
じゃあそのまま今日は、シリーズ1の
one library at a timeというところですけど、
この記事の半分まで昨日読んだので、
今日はその続き、残り半分を読んでいこうと思いますけど、
ちょっと半分だけなので、今日短く終わってしまうかもしれないですね。
ご了承いただければなと思います。
では早速入っていきましょうか。
昨日はAppStoreVRのところですね、 まだ読んだよという感じですね。
ポストCSSを見たりしてて、
実行速度のプロファイリングを見ていたんですね。
パフォーマンスが悪かったのでブレイクを出したよというところですね。
もう1個、SVGですね。
SVGOというライブラリがあると思います。
SVGを使う人たちは、そのような最適化だったりとかでよく使うと思います。
全世界の人が使っているライブラリだと思うんですけど、
そこの中でやっぱりプロファイリングを取って、
実行してみた結果ですね、
特に時間がかかってそうなというものの1つに、
stronggroundというメソッドがあるんですね。
このメソッドの処理をザーッと中身を見てみると、
結局無駄な処理が入っていたりとか、
これこっちの方が良くないとかですね。
分かり切った結果に対して、
いろんなパターンを考慮した処理が入っていたんですね。
それが正規表現で入っていたんですけども、
完全に分かり切っている結果が見えているという処理があったので、
それはもう完全に正規表現じゃなくて、
普通にJavaScriptの別の関節から速くないというので、
結果をやってみましたと。
するとこの人が実行した環境下では、
1.4秒ぐらい速くなっているという事ですね。
めちゃめちゃ速くなったというところで、
すごいなと思いましたという話ですね。
それのプルリクエストも出していますよと。
正規表現周りの処理って全然遅いんですよねというのがあったり、
あとリプレイス処理とかを教えたんですよね、正規表現で。
ガツンと値をリプレイス処理していたんですけども、
それも正直に言うと遅くなってしまうので、
なるべくは正規表現の処理は入れない方が本当に速いなとか、
あと重複して処理の実行を行っていたんですけど、
その片方をですね、重複しているので、
処理自体が無駄になってしまっているものもあったんですね。
必ずどの関数もその処理を取らなきゃいけなくて、
100%無駄なところを通っていたというのがあるので、
03:00
その無駄も省いて、
ちゃんと本来やりたい適切なところを出て、
適切な処理を走らせるみたいなことをできたという事ですね。
それをやった結果、また速度が0.9秒速くなったという事で、
結構バカにならないスピード改善が行われたんだなという事です。
ポストCSSとかSVGOが速くなったのは、
この筆者の方のプルリクの訳なんだろうなって、
つくづく思いました。
今日はまた続きですね。
違うセクションに入っていきたいと思いますけど、
もうちょっとでもSVGOの話が続くかな、これは。
続いて、インライン関数とインラインキャッシュ、
そして再規制の話が来るそうですね。
じゃあ早速今日もそのまま入っていきましょう。
モンキーズという関数はその名前からして興味をそそられるものでしたと。
モンキーズという名前の関数があったんですね。
トレースしてみると、この関数は自身の内部で何度も呼び出されていることがあり、
ということはこれは再規制のことですよね。
これはある種の再規が起こっていることを強く示唆するものになります。
これはある種の再規が起こっていると、
起こっていることを示す強い指標ですけど、
木のような構造を辿るためによく使われますと。
ある種のトラバーサルが使われるときはいつも、
それがコードのホットなパスに当たる可能性がありますと。
すべての話は何より当てはまるわけではありませんけど、
私の経験ではこれは良い経験則でしたというので、
すべてのパターンにあるとは限らないですけど、
この必至なパターンの経験則では大体当てはまるよというパターンですね。
というわけでその処理を見てみているんですけど、
ファンクションペットアイテムというところですね。
ペットじゃない、パーアイテムですね。
パーアイテムという関数が定義されていて、
引数にデータ、インポ、プラグイン、パラムズ、リバースという
5つの引数を取るんですね。
その中にファンクションモンキーズというのが定義されていますと。
これはその今受け取った引数から、
アイテムズというものを加工した値を渡してあげて、
それを引数に取ってガチャガチャなんかやるということですね。
アイテムズ.チルドレン.フィルターで、
とりあえずデータをフィルタリングしてますと。
リバースとアイテム.チルドレンがトゥルーであれば、
モンキーズメソッドをもう1回実行してあげようと。
引数のアイテムズをそのまま渡してあげるということですね。
それをやった後に、
レッド.ケプトというところで変数で値を持ってますけど、
ケプトイコールトゥルーになってますね。
これはフラグ的な変数ですね。というのが関数の中で持ってて、
これがメインフィルターだというコメントがついてますね。
プラグイン.アクティブというところがトゥルーだった場合は、
そのプラグイン.ファンクションという関数があるので、
その関数を実行してあげて、
引数はアイテムとパラムズとインフォですね。
実行結果がフォルスではない場合は、
そのケプトで与えようと。
フラグイン.関数の中でフォルスかどうかの判定をした結果を、
そのケプトという変数に今入れてるわけですね。
また、そのケプトは使うんですね。
最後ダイレクトパスとして、
リバースがフォルスかどうかというのを見てます。
リバースがフォルスであれば、
またはアイテム.チルドレンが存在するのであれば、
06:01
またモンキーズのアイテムというのを実行して、
引数にはまたアイテムを出してあげますと。
その結果、最後リターンでケプトを返してあげてる感じですね。
そのケプトを返してあげたら、
そのファンクション.モンキーズのメソッド自体のイベントは終了して、
最後に大元であるパワーアイテムという関数の
リターンにアイテムを返してあげていると。
あ、違いますね。
リターンでモンキーズのデータを返してあげてるのか。
リターンが3つも重なっていて、ちょっと分かりづらいですね。
可能性がよくないな。
はいはいはい。というところをやっています。
あ、そうか。一番内部のところはフィルターですね。
フィルターした結果、最後返すのはリターンのケプトで、
ブーリアンの値を返してあげているというようなフィルターをしている。
それが返ってきた値をさらに返すのが
ファンクション.モンキーズで、アイテムで返している。
要は内部でガチャガチャ計算処理とかやっているので、
結果的にはフィルターしたりとか、
モンキーズの関数の再起処理的なものをやっている感じですね。
これは確かにパッと見た感じ、
ソースコードの意味が分からないのと、
フィルターの中でやっているリバースかつ、
アイテム.チルドレンの分岐処理をやっているんですけど、
リバースがトゥルーだろうがフォルスだろうが、
どっちにしろ2回走らせたりしているので、
これなんかちょっと闇というか、
改善の余地がありそうなというふうには思いますね。
パッと見た感じ、キナ臭いなと思いました。
あとコメントがちょっと欲しいな。
もう少し。
この引数なんでこんな風に使っているのとか、
ここでリターンするものなんだろうみたいなのが、
パッと見の変数名では分かりづらいので、
もうちょっとコメント欲しいなと思いますけど、
そのまま読んでいきましょうか。
ここで関数がその本体の中に別の関数を作って、
その関数が内部の関数を呼び出すというものです。
推測するに、これは全ての引数を再び渡す必要がないため、
キーストロークを節約するために行われたものと思われます。
はあ、確かにね。
しかし他の関数の内部で作られる関数というのは、
外部の関数が頻繁に呼び出される場合、
全体的化するのはかなり難しいというところですね。
というところで、中身を書き換えてみました。
さっきのパワーアイテムという関数の中身を書き換えて、
処理を変えてみました。
以前にクロージャーでキャプチャーするのではなくて、
全ての引数を明示的に渡すことで、
内部関数を排除することができましたよと。
この変更の影響はかなり小さいんですけど、
結果的に合計で0.8秒の節約になったというところですね。
リターンする後も1個減るし、
さっきのモンキーズという関数自体が1個減るので、
全然いいんじゃないかという感じがしますね。
大元の自分自身をもう1回呼び出す方にシフトすれば、
明示的に引数渡しですね。
やれば同じことができるのでいいんじゃないかということですね。
ちなみにこれは新しいメジャーリリースである3.0.0に対処されていますけど、
エコシステムが新しいバージョンに切り替わるまで少し時間がかかったと
思われますよということでした。
これについてはこの筆者の方がプレリコを出したわけではないということですね。
SVGOのメジャーバージョン3.0.0でこれが対応されて、
またちょっと早くなったというところですね。
09:00
やっぱりSVGOもちゃんとパフォーマンスマイナチュアで
しっかり見ていたということなので、
メジャーバージョン3.0.0以上に上げていない方は
上げたほうがいいんじゃないかなと思いましたね。
多分かなり早くなっていると思います。
では続いてですね、次のセクション。
For of Transpirationですね。
For ofのTranspirationに注意してくださいということだそうです。
ほぼ同じ問題ですね。さっきと同じような問題が
今度はVanilla ExtractのCSSですね。
最新のライブラリー、最新というか今モダンな
かつ人気の高いライブラリーですね、Vanilla Extract。
これ多分今年も伸びるんだろうなと思いますけど。
こちらでも同じ問題が発生していて、
公開されたパッケージには以下のようなコードが同梱されていますと。
今回見ているのはクラスが1個同の定義されて、
コンディショナルルールセットというクラスがあって、
その中にメソッドが1個定義されています。
これはGetSortedルールセットというやつですね。
Sortedされた結果のメソッドを返すやつですかね。
というのが定義されていますと。
中身にはファンクションのループというのが定義されていて、
クエリとディベンデンスがあって、
何か処理をするよということですね。
その結果をまたループに入れていると。
ファンクションループという関数名があって、
でも変数名もループだというので、
ちょっとこれ関数定義を単にしているだけという話か。
あれば別に定義のところに名前を付ける必要はない気がしますが、
まあいいや。
その関数を何度も何度も実行している感じですね、これは。
this.presidentlookupのエントリーか。
オブジェクトのループを回していて、
それをクエリとディペンデンスという値で受け取っていますと。
それを元にループ関数にかけて何かわちゃわちゃやっているということですね。
というのをコンディショナルルーセットというクラスのメソッドの中でやっていると。
まあやっていることはわかりますが、
確かに元のソースコードには存在しないのが確かにこの数の面白いことですね。
原点ではループの点々というのが4ofのループの規格になっているので、
この辺の処理が、
4of自体が確かそもそも遅いよねって話なので、
ここに確かにパフォーマンスが遅くなっているなというところの問題が発生しそうな気がしますね。
バベルとかタイプスクリプトとかのリプルでこの問題を再現することはできませんでしたけど、
彼らのビルドパイプラインによって導入されたものであるということが確認できましたよと。
ビルドツールの抽象化を共有しているようなので、
もう少し多くのプロジェクトはこの影響を受けているものと思われます。
というわけでとりあえずノードモジュールズのローカルでパッチを当てたところ、
ビルド時間がさらに0.9秒改善されたので満足ですと。
これはブルーリンクを出してはいないわけですし、
ちょっと影響範囲が広いのでやめているということですね。
一応確かにさっき見ているソースコードを見たんですけど、
変数名の頭がバーから始まったりアンスコループだったりするんです。
これは確かにバベルとかトランスファイルした後の結果のソースコードがそういうことを吐き出している感じがするので、
その結果の前のところにコミットするのは影響範囲があったりするので、
今回この筆者の方は試しにビルド結果の後のノードモジュールズですね。
12:01
持ってきたモジュールの中身の方を見てみて、その中身に手を加えてみたと。
それをやった結果0.9秒早くなったということですね。
ちょっとこれは難しいですけど、
ただ本当にちゃんとパフォーマンス改善をやりたいんだったら、
ノードモジュールズですね、NPMから持ってきたもののコード自体に手を加えるっていうのは、
なくはないですけどちょっと怖いですよね。
また改めて新しく誰かが、別のメンバーが入ってきたときに、
そうやらないで、その人だけずっと処理が遅くなったりするみたいなのがあったりするので、
情報共有ですね。ちゃんとチーム内にこういうことをやりますよというので、
プロジェクトのリードメインにしっかり書いておくとかですね、
オンボーディングのところでそこの情報を共有するっていうのをやらないと、
ちょっとテクニカルな感じがします。
ただ、とはいえですね、こんなに早くなるのであれば無視はできないなっていうので、
これは確かに僕今までやったことなかったんですけど、
今後の選択肢の一つに入れようかなと思いました。
なので依存ライブラリーがどれだけ有名なものだったり、
いろんな人がコメントしたり、GitHubスターがついたところで
パフォーマンスが早いかどうかっていうのは別の話ではあるのでし、
この何日間ずっとスピーディングアップザファスクリプトエコシステムという記事を読んでるんですけど、
どのライブラリーでもまだまだパフォーマンス改善の余地っていうのは
いくらでも隠れてるんだっていうのはつくづく思いました。
ので、その辺を追っていくのは確かにいいかもなっていうのは思いましたね。
1個1個のライブラリー、有名どころだとしても、
自分自身で実際にプロファイリング撮ってみてみると、
いろんなものが見えて面白いかもしれないですね。
では続きまして、センバーですね。
センバーの不思議なケースっていうところのセクションに入ります。
この件に関しては何か設定が間違っているのかよくわかりません。
基本的にプロファイルっていうのはファイルをトランスファイルするたびに
バベルの全コンフィギュレーションが常に新しく読み込まれるってことを示しました。
バベルはそうですね。
全てのコンフィギュレーションが常に新しく読み込まれ直すんですよ。
それ自体は結構オーバーヘッドな感じがしますけど。
その話もしました。
続けていきますが、
プロファイリングの画像ファイルがペッと貼られて、
また今回も重いのが1つ見つかりました。
ちょっとスクリーンショットではわかりにくいんですけど、
多くの時間を割いている機能の1つに
NPMのCLIと同じパッケージであるセンバーパッケージのコードがありました。
正直この日は驚いた。
センバーがバベルと何の関係があるんだとか思っていて、
思い当たる節があるとすれば、
バベルスラッシュのプリセット円部の
ブラウザリストのターゲットがパースするためのものですよ。
ブラウザリストの設定はかなり短く見えるようにしませんけど、
最終的には約290個の個別のターゲットに拡張されていました。
それだけ気にするほうではないんですけど、
290個という個数であれば、
別にコンピューター的には全然面白いじゃないんですけど、
検証関数を使うと配分コストを見逃しがちなので、
そこに注目をしました。
バベルのコードベースでは少し広がっていますけど、
基本的にブラウザターゲットのバージョンは
センバー文字列10から10.0.0に変換され、
バリデーションされています。
15:00
これらのバージョン番号の中には
既にセンバーのフォーマットと一致しているものもあります。
ただ場合によっては、
バージョンの範囲をトランスファイルが必要な
超高倍数の機能セットを見つけるまで
互いに比較してしまって、
このアプローチには何の問題もないよということを言っています。
パフォーマンスの問題はここにあるということです。
センバーのバージョンでは解析された
センバーデータタイプではなく、
文字列として保存されるため、
ここでパフォーマンスの問題が発生します。
つまり、センバー.validで
括弧に引き継ぐ1.2.3を文字列で渡したとしましょう。
これを呼び出すたびに新しいセンバーインスタンスが作成されて、
すぐに破棄されてしまう。
文字列を使用しているときに
センバーのバージョンを比較する場合も同じようなことをやっています。
センバー.ltですね。
1.2.3と9.8.7みたいな文字列で渡してあげて実行する。
これがトレースでセンバーが目立つ理由になります。
なるほどですね。
再度、ノード文字列でローカルにパッチすることで
さらに4.7秒ビルド時間を短縮することができました。
すぐに破棄してしまうのはちょっともったいないですね。
一度センバーインスタンスを作ったら
それを使い回したほうが絶対に早いと思いますね。
.validとか.ltとかいろんなメソッドで使うと思うので
そういうことをこの辺はローカルの中でやったら
4.7秒早くなったということですね。
これも割とパッチ当てたならプルリグ出してほしいですけどね。
ここまで改善したならあればですけど。
じゃあ最後はちょっと難しい内容で
今日もあれだったんですけど
コンクリューションに行きたいと思います。
この時点で私は見るのをやめましたと。
他にもたくさんあるし
同じようにプロファイリングを取ってみれば
見れることがたくさんあるんでしょうけど
一旦この人はPostCSSとSVGOと
その他もいろんなライブラリありますけど
複数2,3個ですね。
あともう2,3個見て終わったようです。
一般的なライブラリにはこのような小さなパフォーマンス問題が
もっとたくさんあるようには思われます。
今日は主にBuildツールを見ていきましたけど
UIコンポーネントや他のライブラリにも
大抵同じような性能的な問題がありますと。
これでGoとかRustの性能に匹敵するのでしょうかというのは
本来の問いですね。
最近はRustとかGoの対等がすごく大きくて
この辺は速くてJavaScriptは遅いよねっていう話があって
JavaScriptの処理自体は確かに遅いんですけど
実行速度自体が遅いんですけど
でも遅い理由はパッケージだったり使い方だったりとか
ソース行動が悪かったりするんじゃないのっていうのが
この人の発端というかスタートの観点だったので
じゃあ現代に戻ってGoとかRustの性能に
やっぱり匹敵するのかということですけど
結果的には言語速度があるので
可能性はやっぱり低いですよとおっしゃってます。
しかし現在のJavaScriptのツールは
今より早くなる可能性は全然あるので
一概に悲観的な目線で見る必要もないなということです。
そしてこの記事で調べたことは
多かれ少なかれ評査の威嚇に過ぎないので
皆さんの方にも注目してくださいと。
一応続いてこのパート2のシリーズに続きますよ
というのでこの記事が締められておりました。
18:00
パート2は前回読んでいったモジュール解決のところです。
NPMでインストールした後に
パッケージモジュールをインポートするか
それとも相対パスでインポートするか
絶対パスでインポートするかという
3つのモジュール解決のパターンがあるんですけど
この3つのパターンについて
やっぱり一番遅くなったりする可能性が高いのは
パッケージそのものをインポートするやつですね。
NPMから持ってきたパッケージをインポートするパターンが
やっぱりパフォーマンス遅くなる余地が
すごくありそうだよねっていうところを見てました。
この記事もすごく素晴らしいので
ぜひ皆さんの方でも読んでみていただければなと思います。
じゃあ今日はですね
パート1 PostCSS SVGO & Many Moreという記事ですけど
これの後半を読んでいきました。
はいじゃあ明日はですね
最新のパート4ですね
NPMスクリプツのところのパフォーマンス改善の
お話を読んでいこうと思います。
はいNPMスクリプツもちょっとあれですね
プロファイリング見たことないんですけど
プロファイリング画像ちょっとパッと貼られてるの見たんですけど
ローディングゼNPMバイナリーズエクスペンシブ
って書いてるくらいめちゃめちゃ時間かかって長いので
恒例の改善がどれだけできたかちょっと気になるので
また明日もパフォーマンス改善の記事を読んでいこうと思います。
はいでは今日の朝方はこちらで以上にしたいと思います。
今日はNEVさんですねお参加いただきありがとうございます。
はい金曜日ですねしっかり今日も締めていただいて
また土日休んでいただけたらと思います。
というところでもう3月も終わりますね本当に
3月今月の最終?そうですね
今月最後の土日がもう目の前って感じなので
はいいや早いなびっくりですね
うちはその12月目なのですでに第一四半期が終わるというところで
いや本当に今年の進捗大丈夫かって思いながらですけど
しっかり3月の振り返りもしつつ
また4月から新入社員も入ってくるので
気がいけて切り替えていけたらなと思います。
はいじゃあ終了したいと思いますお疲れ様でした