1. kkeethのエンジニア雑談チャンネル
  2. No.203 朝活「Speeding up the..
2023-03-28 18:21

No.203 朝活「Speeding up the JavaScript ecosystem - one library at a time」をダラダラ読む回

はい.第203回は


Speeding up the JavaScript ecosystem - one library at a time



を読みました💁

順番は前後しますが,このシリーズ1本目の記事ということもあり,最初の導入部分かなぁと思ったら1本目から知だらけでした😂とても興味深い内容ですので,ぜひ皆さんも読んでみてください!


ではでは(=゚ω゚)ノ

See Privacy Policy at https://art19.com/privacy and California Privacy Notice at https://art19.com/privacy#do-not-sell-my-info.

00:03
はい、3月23日木曜日ですね。 時刻は朝9時13分になりました。
どうも風吹いたっぽくて、まあ頑張っていきたいと思いますが、
なのでちょっと今日は鼻声だったりするので、もしかしたら、あの、
苦しいかもしれないですけど、あの、ご了承いただければ、まあ幸いですと。
が、今日はタイトーになりますシリーズですね。
Speeding up the JavaScript ecosystem で、それの、今回、これシリーズパート4まで分かれてるんですけど、
パート1ですね、今日は入っていきたいと思います。
このシリーズ知ったのがパート3ですね、3から知ったんですけど、
そこから2、1と今下がっていって、で、新しいのがパート4があるんですけど、
これはまた明日以降、読んでいこうかなと思っております。
で、今日はちょっと、変則的ではありますけど、パート1ですね。
はい、いきたいと思います。
ポストCSSとかSVGOとか、AND、まあメニューもあって書いてあるんで、まあいろんなライブラリー周りのところですね。
はい、まあこのシリーズの、あの、一番最初、パート1ですので、
まあホッタンとなるお話のところが、入ってくるとは思うんですけど、
まあ、読んでいこうかなと思います。
じゃあいきましょう。
ほとんどの一般的なライブラリーっていうのは、その不要な型変換を避けたり、
関数の中に関数を作らないようにすることで一応高速を作る、高速化することができますよというのが、まあTLDRだそうですね。
はい、で、JavaScriptのビルドツールを、ラストやGOなどの他の言語で置き換えることが、
まあなんかトレンドになっているようですけど、
まあ現在のJavaScriptベースのツールっていうのは、もっと高速化できるはずですよねと。
で、典型的なフロントエンドプロジェクトのビルドパイプラインっていうのは、
通常多くの異なるツールっていうのが連携して構成されていますと。
しかしツールが多様化することで、ツールのメンテナーっていうのは、
自分のものがどのツールと一緒によく使われているのかっていうのを知っておく必要があるため、
パフォーマンスの問題を発見するのが少し難しくなってますねと。
で、純粋な言語としての観点から見れば、JavaScriptってのは確かにラストやGOよりも遅いのは事実としてあります。
が、まあ現在のJavaScriptのツールはかなり改善することもできますよと。
確かにJavaScript自体は遅いんですけど、
今のような比較ではそこまで遅くはないはずですと。
最近のJITエンジンってのはめちゃめちゃ速いですからねっていうふうに申し上げますと。
で、好奇心から一般的なJavaScriptベースのツールのプロファイリングに時間をかけて、
その時間がどこに通用されたかっていうのをちょっと確認してみましたよと。
で、まずはそのCSSのパーサーとかトランスファイラーとして非常に有名なPostCSSから始めましょうっていうのが冒頭の話ですね。
このシリーズ、先に進んでいけば、次あれですね、
モジュール解決と、で続いてパート3がESLintですね、のところまで行くので、
JSのほうのライブラリを見ていくんですけど、今回は最初CSSのところから入るというので、
PostCSSをまず見ていこうという話です、だそうです。
はい、じゃあ最初ですね、セクション1ですけど、
セービング4.6秒ですね、in PostCSSと。
ほう、そんなに改善できるんですか。
PostCSS Custom Propertiesというような非常に便利なプラグインがあり、
古いブラウザーのCSSのカスタムプロパティの基本的なサポートを追加してますと。
このプラグインというのは内部で使用する、正規表現に起因するコスト、4.6秒ですね。
かかっていて、なぜか痕跡の中で非常に目立つように表示されました。
そしてそれはとても奇妙にも見えましたよと。
03:02
はい、実際その4.6秒時間かかっているよというところですけど、
あの画像、キャプチャーが貼られてますね。
プロファイリング撮ったやつ、ブラウザーでプロファイリングしてると思うんですけど、
それのキャプチャーがガッと貼られていて、
レグエクスポンのなんちゃらかんちゃらみたいなところですね。
に、ポストCSSでカスタムプロパティというのがあって、
それが時間がかかってとても奇妙に見えたので、
そこにちょっと着目をしてみましょうと。
この正規表現というのは特定のコメントの値を検索して、
プラグインの動作を変更するものになりますので、
ESLintは特定のLintルールを無効にするために使用するものにちょっと似ていますねと。
Readmeには記載されてませんけど、ソースコードを覗いてみると、そのように推測されました。
正規表現が作成される場所というのは、
CSSルールや宣言の前にコメントがあるかどうかをチェックする関数の一部になりますと。
なんか一個関数が貼られてますけど、
function isBlockIgnoredというのがありますね。
はぁはぁはぁ。
ブロックを無視するかどうかというのを判定する関数ですね。
で、引数にRuleOrDeclarationという引数で変数を受け取ると。
で、constRuleイコールその今受け取った引数ですね。
RuleOrDeclaration.Selectorで見ていて、
これ30年あれですね。
審議値を見ているとですね。
TrueSeeかFalseSeeかだけを見ていて、
もしTrueSeeだったらそのままRuleOrDeclarationというのをルールに入れてあげれば良いと。
そうじゃなければRuleOrDeclaration.Parentというのがあるらしいので、
そのParentの値を持ってそれをルールにしましょうと。
で、最後にReturnで正規表現がガチャガチャ書いてあって、
.test、テストというのはJavaScriptのあれですね。
正規表現を実行するテストメソッドというのがあるんですけど。
この関数にRule.toStringですね。
Ruleの中身を文字列に変換をして、
それを渡してあげて、正規表現でチェックをしているということですね。
はぁはぁ。分かりました。
やっていることはそんなに難しくなくて結構シンプルですね。
で、それを見ているんですけど、
それをやった結果Rule.toStringの呼び出しというのはすぐに私の目を引きました。
まあtoStringメソッドって確かにちょっと重いんですよね、確か。
ある方を別の方にキャストするというのは、
パフォーマンスに取り組んでいれば通常もう一度見る価値がありますと。
このシナリオで興味深かったのは、
そのRule変数が常にカスタムtoStringメソッドを持つオブジェクトを保有していることですと。
元々文字列でなかったので正規表現をテストするためにシリアライズコストがかかっているのは分かりますと。
それもそうだよね。
またオブジェクトとかいろんな形の変数の中身になっていると思うので、
それを正規表現でチェックするには確かにテキスト型にしなきゃいけないのでシリアライズすると。
そのためのコストがかかるのはしゃしゃあないと思いますけど、
経験上、多くの短い文字列に対する正規表現のマッチングというのは、
少数の長い文字列に対するマッチングよりもずっと遅いことが分かっていますと。
短い文字列に対する正規表現のマッチングというのが、
数は少ない長い文字列に対するマッチングよりもずっと遅いと。
結局量に比例するということですね。
というのがこの人の経験値ですけど。
これは最適化されることを願っている最有力候補だよなというふうに思ったらしいですね。
なるほどでした。
このコードの厄介な点というのは、ポストCSSコメントがあるかどうかに関係なく、
06:00
全ての入力ファイルがこのコストを支払わなければならないという。
ああ、そうですね、確かに。
必ずこの処理を通らなきゃいけないというのは確かにそうですね。
なので、別にコメントがないんだったら、
この処理を走らさないだけでパフォーマンスが上がるんじゃないの?
というのがこの方のおっしゃっていることですね。
はい、長い文字列に対して一つの正規表現を実行する方が、
短い文字列に対して繰り返して正規表現を実行するよりも、
シリアドライズコストが安いことを知っているのであれば、
この関数をガードして、ファイルにポストCSSコメントが含まれていないことが分かっていれば、
isBlockingいるのは呼ぶ必要すらないようにすることができますよと。
で、ちょっと今回は具体的なソースコードはないんですけど、
実際にそれをやってみた結果、
ビルド時間はなんと4.6秒短縮されましたよということですね。
めちゃくちゃ早くなりましたね、4.6秒。
どれだけでかいプロジェクトのプロファイリングをとっているのかちょっと分からないですけど、
あ、でもこれはあれか、ポストCSSですもんね。
まあそうするとCSSのファイルがどれくらいあるかとかちょっと分からないですけど、
まあでもこの方の中では4.6秒早くなったよと。
まあどんな環境かは分からないんですが、
その環境のですね、リンクとかが特になさそうですね、残念ながらですね。
まあその環境があればよかったんですけど、
一応ですね、そのポストCSSの今見ている、
そのカスタムプロパティーズっていうプラグインですね、あるんですけど、
まあそれの一応リンクは貼られているんで、そこを見てみてくださいってことですね。
もしかしたらそのリンク先の中のテストを走らせていたのかもしれないですが、
結果的にこの人の環境の中では4.6秒早くなったってことで、
すごく早いですねやっぱり。
正規表現でチェックするためのテストレーニングとしてリアライズコストって
結構やっぱりかかるんだなっていうのはつくづく感じますね。
まあ一旦今のが一つの例でした。
で、次2つ目はオプティマイティングSVGコンプレッションスピードですね。
SVG圧縮速度の最適化をしたってことですね。
はい、次はSVGファイルを圧縮するためのライブラリ、SVGOですね。
これSVGOと読むのかSVGOと読むのか未だに僕も分かってないんですけど、
僕はSVGOと読みます。
この記事でとりあえずですね。
SVGアイコンを対応するプロジェクトでは定番のかなり素晴らしいライブラリです。
CPUプロファイルを見るとSVGの圧縮に3.1秒費やされていることがわかります。
これを高速化することができないのかなというところですね。
まあそうですよね、これ早くなればそれに保障ないと思うんで。
プロファイリングデータを少し調べてみると、
ストロングラウンドという一つの関数が目につきましたよと。
その関数の後には必ずGCクリーニングアップが行われています。
GC、ガベッジコレクションですね。
画像を貼られていますけど画像の中で赤枠になっていて、
そのGCコレクションが度々発火されているわけですよね。
ここをちょっと気になったので見てみようというお話だそうですが、
考えてみようと。
私の好奇心が刺激されたと思ってくださいと。
GitHubでソースを引っ張っていきましょうというので、
この実際のこのストロングラウンドというメソッドですね。
これが気になったので見てみましたと。
GitHubのリポジトリもそのままリンクが貼られていますので、
この関数そのまま見てみますと。
関数自体は結構短いですね。
10行ちょっとぐらいしかないやつなんですけど、
ファンクションストロングラウンドで引数にはデータを受け取りますけど、
これは数字の配列ですね。
ナンバー配列が渡されるものですよと。
で、4分でiイコールデータドットレングスで
09:04
iマイナスマイナスで行くということが書いてあります。
0より大きいところまでずっと4分を繰り返しますと。
で、ifのデータのiですね。
i番目のデータの値をtoFixedProcessionですね。
で、見てデータのiと比較をしていますと。
ちょっとこれ古いコードらしくて、
厳密なチェックじゃないですね。
ビックリイコールなので、
イコール2つじゃないから型自体は見てないって感じですね。
ソースコードも見ますけど、
TS行くのはTS行くのはTS行くのはがーっと書かれてますので。
どこをかしくとってもTS行くのはでいいな。
TSが入っているけど、
ちょっと無視されるような形になっているらしいですね。
これちょっと悩ましいところではあるが、
まぁまぁ一旦置いておきましょうか。
SVGOみたいな、
全世界みなさんが使っているようなライブラリーですら、
こういうことをやっているので、
TSの使い方、原理主義者の方々ってどうなんだろうって思ったりはしますね。
やっぱり時と場合によってはこうやって、
少しはignoreをして、
で、ハショルというか、
開発のところのコストをかけないみたいなのも
少しはアリだとは思いますけどね。
もちろん厳密に全部型定義でチェックできるのが、
本当は理想ではあるんですけどね。
まぁ難しいところではあります。
その2FIXで、
if文でチェックをしているからですね。
ゆるい型チェックをしていて、
データの相場名の2FIXとprecision-1ですね。
で、一応見てますと。
precisionという値で何が入ってくるかは
ここは今、想像的には分かっていないんですけど。
とりあえずその変数があるんですね。
このTAS全体の中でどこかで持っている変数があって、
その値を、
-1か。
まぁとりあえず1個前なんでしょうね。
という値を見て、
FIXとしていますと。
それ以上プラス型はについているので、
数字変換をしているんですかね。
なんかわちゃわちゃ他のこともやってますけど、
まぁ他、
音読だとやっぱり、
ソースコードを音読ってやっぱり分かりづらいと思うので、
ちょっと端折りますけど、
まぁなんか色々ガチャガチャやってますよってことですね。
渡された数字引数の配列を持って、
その配列から、
なんか、
計算をした処理の結果ですね。
のデータ配列を改めて返しているってことですけど、
まぁちょっと本文戻ります。
えーと、
典型的なSVGファイルには、
たくさんある数値を圧縮するために、
まぁ使われる関数ですよと。
あー、そっかそっか。
SVG時代は確かに、
見たら分かりますけど、
文字列もいっぱいありますけど、
数字も結構ガチャガチャ出てくるんですよね。
ていうかまぁ数字だらけなんですけど、
まぁこれを圧縮するために使われる関数が、
この、
ストロングラウンドってことだそうですね。
で、この関数は数値の配列を受け取って、
そのエントリーを変異させることが、
まぁとりあえず期待されています。
で、この関数の実装で、
使われている変数の種類を見てみますと、
えー、少し詳しく見てみますと、
文字列と数字の間で、
多くの行き来があることに、
まぁ気が付きますと。
数字を丸めるっていう行為が、
この中に入っているんですね。
で、この行為は、
数字を文字列に変換することではなくて、
ちょっとした計算だけできることのように、
まぁ思われます。
まぁ一般的な経験則として、
えー、最適化の大部分は、
物事を数字で表現することに、
まぁありますよと。
で、主な理由は、
CPUが数字を扱うことに、
非常に長けているからです。
CPUは数字を扱うの得意だからですよ、
ということですね。
文字列よりも、
CPUは数字の方が扱いに長けているんですね。
うーん、
コンピューターは01なので、
まぁ数字の方が慣れている、
まぁそうなのかもしれないな、
12:00
ということですけど。
続いて、
よく使われている、
このファンクション、
トゥフィックスですね。
トゥフィックス関数っていうのを、
ちょっと見ていたんですけども、
これもちょっと注目しましょうと。
で、再度プロファイリングを実行したところ、
ビルド時間を約1.4秒高速化することになりました。
これも上流で、
プロリクエスト出しておりましたと。
もう一緒に、
これもプロリクエストを出したよ、
ということだそうですね。
トゥフィックスのところですね、
関数も、
ちょっと自分なりに、
アレンジしたらしいですね。
というメソッドですね。
ナンバーオブジェクトの中の、
組み込みプロの関数ですね。
トゥフィックスってあるんですけど、
このままだと、
ちょっとコストがかかるんで、
ウィズアウトキャスティングって書いてますね。
キャスティングなしの、
文字列を返すような風に、
ちょっと書き換えてみましたよと。
こういうことをやってみました結果、
1.4秒速くなっているところですね。
ちなみにそっちの方が良くないというので、
プロリクエストも出したと。
ほんとこういうところですよね。
OSSってこういうことを言うよなって、
つくづく思います。
ちなみにですけど、
この出したプロリクエストですけど、
ちゃんとマージされてますね。
しかも議論もそんなに長くなく、
コメントぐらいでわざわざOKだなというので、
マージをされていると。
実際にパフォーマンスもかなり上がって、
速くなったじゃんみたいなので、
みなさんグッと出してたりして、
とても素晴らしいことだと思います。
SVGOにこれだけプロリクエストを出せると、
やっぱり強いなと思いました。
続いて、REGX is on short stringですね。
パート2。
なるほど。
さっきのやつの続きになりますね。
ストロングラウンドの続きですけど、
このすぐ近くに1秒近く、
約0.9秒かかっている関数があって、
こっちも怪しいと思いましたよということですね。
これは何かというと、
同数点以下の数値で、
1より小さく、
マイナス1より大きい場合は、
先頭の0を削除することができるという特徴があります。
はぁはぁ、なるほどね。
つまり、0.5は0.5、
マイナス0.2はマイナス0.2に圧縮できる
というところですね。
特に最後の行はとても興味深いという話をしています。
それを、
stringifyNumberという関数を定義されていますけど、
第一引数Number、
第二引数PrecisionにNumberですね。
というのを渡されていて、
Number.toStringに変換して、
to.replaceですね。
replaceの中でその0というのを弾いて、
改めてreplaceとやっていますね。
プラスの方の0と、
マイナスがついた方の0というのを
取るというところを
強弦でわちゃわちゃやっている感じですね。
replaceを2回走らせて、
プラスとマイナスの方の
頭の0を削るという処理を
正強弦でやっています。
ここでは数字を文字列に変換して、
数字の文字列バージョンというのは
短い文字列である可能性が極めて高い。
ある数値がNが0より大きい、
かつNが1より小さいと、
Nがマイナス1より大きい、
かつNが0より小さいの両方を
同時に満たすことはできない
ということも知っています。
なおかつNumですね。
ノッターナンバーのNumですけど、
でさえもその力はないと。
このことから正強弦はどちらか1本にしか
マッチしないか、どちらにもマッチしないかの
どちらかであるため、
1回と数字のことは一応できます。
15:01
リプレイスの呼び出しのうち、
少なくとも1回は常に無駄になっています。
確かにそうですね。
両方で確かにいっぺんにチェックしたいんですけど、
両方を走るとは限らないんですよね。
両方とも同時に走ることは
まずあり得ないので、
必ず片方は無駄な処理を
走っているということなので、
これも通用値があるようなことでした。
このようなケースを手作業で区別することで
最適化することができます。
先頭の0がある数字を扱っていることが
地下ロジックを適用するように
わざとしましょう。
このような数値のチェックは
正強弦による検索よりも短時間で済みませんので、
ストリングファイナンバーの関数の中身を
ちょっと書き換えています。
最初にナンバー.トゥストリングをやって
ストリングナンバーという別の変数で受け取っています。
IFですね。
そのものを持っているNUMというやつがあるんですけど、
NUMが0より大きいまたは1より小さい場合は
リターンのストリングナンバーですね。
さっき言った
そのトゥストリングで受け取った値を
ドットリプレイスでやります。
ELSEIFでやって
マイナス1より大きいかつ
0より小さい場合ですね。
その場合はマイナスの0が付くので
ストリングナンバー.リプレイスをやっていると。
単純なことですけど
これをやることで単純に
計算処理が半分になりますよね。
かつやりたいことは同じこともやっていて
単純にIF分岐をしているということです。
これをやりましたと。
さらに一歩進んで正強による検索を
完全に排除することもできます。
文字列がどこにあるか100%確実にわかるので
頭にあるとわかっているので
文字列を直接操作することも
もちろんできますよというので
今みたいな正強ゲームでやるわけではなくて
単純に
ストリングナンバー.の
スライスですね。スライス1を
やってしまえばよくないと。
マイナスが付く場合は完全に
マイナスが付くのでリターンするときに
マイナスプラスで文字列を追加してあげておいて
スライス2ですね。
2つ目の値が0だというのがわかっているので
そこまでカットすればいいということですね。
それをリターンで返してあげれば
よいと。とても素晴らしいことです。
僕もそう思いましたね。
最初から0で分かり切っているのであれば
スライスという正強ゲームのメソッドって
ちょっと時間がかかってコストがかかるんですよね。
それだったら確かにスライスのほうが早かったって
僕も記憶があるので
それのほうがよいんじゃないかという話ですね。
CodeBaseには先頭の0を切り取る関数が
もちろんそもそも用意されているので
代わりにそれを活用することができます。
結果それをいろんなことをやったおかげで
できました。
これもまた出しておきましたよと。
これもちゃんとされていますね。
素晴らしいですね。
カットはやばいな本当に。
ちょうどインラインファンクション
インラインキャッチ&リカージョン
という最期的なお話が続くそうですけど
ちょっと
ソースコードもいくつか出てくるので
このまま走り切りたいと思いますが
すみません。朝9時半を超えてきたので
ちょっと今日の朝活はここで
終了しようと思います。
いかがだったでしょうかね。
とても面白かったし資産に富んだもので
シリーズ1,2,3で分かれているんですけど
一個一個が記事
完結型なので逆流してますけど
全然ちゃんと理解できるし
通じるなというお話でした。
18:01
また本当に技術的に
勉強になる記事だったので
ぜひみなさんも読んでみていただければと思います。
じゃあ今日の朝活はこちらで終了したいと思います。
木曜日
あと
2日しかないので
それでは終了します。
お疲れ様でした。
18:21

コメント

スクロール