00:06
はい、3月19日、日曜日ですね。 時刻は昨日11分になりました。
体調が悪くてですね、今日あんま声変かもしれないです。
おはようございます。ひめめのkeeth、ごとくわはらです。
ではでは、そんな中ですけど、 本日も朝活を始めていきたいと思います。
はい、じゃあ今日はですけども、
すごい技術的な記事を読みたくなったので、 読んでいきたいと思いますが、
今日はSpeeding up the JavaScript ecosystemということで、 eslintですね。
JavaScriptのエコシステム環境周りのところのパフォーマンスですね。
本当に実行速度の話だと思いますけど、
今回その中でも、多分全世界でJavaScriptのシステムの中では、
まあほぼみんな使うでしょうと言われている eslintですね。
のパフォーマンスのところの記事があったので、 それをちょっと読んでいこうかなと思っております。
では早速いきましょう。レノアさんですね。
おはようございます。ご参加いただきありがとうございます。
今日もダラダラとこの感じで読んでいきたいと思います。
しかも時間は9時11分になっているので、
9時半を若干押すかもしれないですけども、 ご理解いただければ幸いです。
じゃあいきましょう。
リンティングというものは、間違いにつながる可能性のある コードのパターンを見つけたりとか、
一貫したリーディング体験を保証したりする行為になります。
多くのJavaScriptもしくはTypeScriptのプロジェクトの 中核を成すものにもなります。
私たちはエレクターエンジンとASTの変換処理に、
時間短縮の可能性を見出したり、
JSTACKから出た完璧なリンターというのは、
秒以下の実行時間を達成することができるでしょう というのがTLDRになります。
この記事はパート1、パート2、パート3と分かれていて、
パート1がPostCSSとかSVGOとかですね、
あと他、anymoreとかですね。が一つ目。
パート2はモジュールリソリューションですね。
モジュール解決のところが二つ目だったそうですね。
ちょっとこれ三つ目から見てます。
この記事自体はパート3のLinting with ESLintということなんですけど、
これあれですね、モジュールリソリューションをちょっと読みたくなりましたね。
パート2から入った方がよかったかもしれないですけど、
これ、次回以降やっていきたいと思います。
今日この記事もしかしたら読み切れない気がするので、
それはまあ明後日からになりますけど、
モジュールリソリューションをこのまま続けて読んでいこうと思いますね。
ではでは、早速本記事に戻りたいと思います。
このシリーズの過去2回の投稿で、Lintingについてかなり話した。
読まないまま、これいきなり三つ目読むのはよくないかもしれないですけど、
すいません、逆流します。
そろそろESLintにふさわしい脚光を浴びせようと思っております。
全体的にESLintというのはとても柔軟で、
パーサーは全く別のものに変換することも可能になります。
JSXやTypeScriptの対等により、
このようなことが頻繁に行われるようになりました。
プラグインやプリセットの健全なエコシステムというのが充実しているので、
おそらくあらゆるユースケースに対応したルールというのが存在し、
もし存在しない場合でも優れたドキュメントが
独自のルールを作成する方法というのをガイドしてくれています。
これは時の試練に耐えてきたプロジェクトとして、
ここで強調していきたいことの一つです。
03:01
エコシステムとかライブラリー周りというのはエコ精髄激しいですし、
特にフロントエンド周りは次の瞬間使われなくなったみたいなことが
本当によくある中で、時の試練に耐えたという言い方は
本当にすごいことだと思いますね。
しかし、これはパフォーマンス・プロファイリングの問題でもあります。
設定の柔軟性が膨大であるため、パフォーマンスのリンチに関しては
2つのプロジェクトが大きく異なる経験をすることがあります。
そこで、ES-Lintのリポジトリで使われている
Lintの設定そのものを調べてみることにしました。
第1セクションに入りますけど、
Using ES-Lint to Lint ES-Lint
ES-Lintを使って、オプションに
Report unused disabled directivesというのがありますね。
というのを付けておいて、ドットを付けているので、
カレントディレクトリ以下を全部見てください。
イグノアパターンでドックススラッシュしているので、
ドックスディレクトリ以下はとりあえずイグノアしてください。
それ以外は全部カレントディレクトリ以下を見ているというコマンドを叩いて
実際にLintを走らせることができます。
その時に実行した結果があります。
ここにあります。
ES-LintはES-Lintを使用してコードベースをLintしているのみです。
このシリーズの前の記事と同様に
ノードの組み込み引数、CPUProfというのが
プロファイリングの略だと思います。
CPUProfというオプションがありまして、
これを使ってAfter.CPUProfというのを作成して、
それをスピードスコープに読み込んで、さらに分析することにしてみます。
数秒後ですね。正確には22秒後になりましたけど、
私たちは飛び込む準備ができています。
一応結果の画像がパッと貼られていますけど、
まずはJSDocルールのところに時間を使われていて、
次はOtherRulesのところに時間を使われていて、
次はセレクターエンジンに時間を使っていて、
また別のOtherRulesが時間を使っていて、
相手最後にコンパースに時間を使われているという
感じの画像になっていますけど、
OtherRulesは2回使われていたりするのと、
セレクターエンジンが意外と時間を食っていますね。
この辺にパフォーマンス改善の余地がありそうというのを
何も知らないままパッと見て思いました。
こうすることで、どこに時間が費やされているかというのを
より正確に把握することができます。
これは一般的に
左偏調の可視化と呼ばれるものになります。
Left Heavy Visualizationというらしいですね。
僕はこの言い方とか意味がちょっと分かっていなくてごめんなさい。
これはX軸がいつコールが発生したかというのを表していて、
一般的なフレームワークグラフトを
混同しちゃいけませんよというのに注意してください。
こちらではX軸がいつ起こったのではなくて
全体の時間のうち消費された時間というのを表しています。
普通は確かにX軸のタイムラインを表していますけど
タイムラインではなくて、どれくらい時間がかかったかというのが
X軸の表されています。
これはスピードスコープの大きな利点の一つで、
より素早く可視化することができます。
この業界では卓越したエンジニアリングで知られる
Figmaの数人の開発者が書いたものですから、
06:00
なるほど、Figmaの中の方々が
スピードスコープというツールを作ったんですね。
そう言われると確かにもう信頼しちゃいますね。
そして僕はスピードスコープを使ったことがあったので
この後使ってみようと思います。
単なるスピードの数字計測ではなくて
プロファイリングに特化したサービスっぽいですね。
これありがたいですね。URLとかをぶん投げたらやってくれそうな気がしますね。
すぐにESLintのリポジトリにある
JSドックセットアップが時間を費やしている
いくつかの主要な領域を把握することができます。
特に目立つのは関数名から推測されるJSドックというのを扱うルールに
全体の時間のかなりの部分が
費やされていることがわかります。
これはESLintのソースコードを見たらわかるんですけど
ESLintってTSで書かれてないんですよね。
やっぱり歴史が長いライブラリーでもあって
プロップタイプとかそういうものも使ってないし
本当に純粋なJSで書かれてるんですけど
そのためにJSドックがかなり細かく書かれていて
ある意味言うと
ソースコードがドキュメントってこういうことだよなぐらいの感じです。
コメントの書き方とか設計の仕方って
ESLintはすごく勉強になるなと思っているので
この辺参考程度にJSわからない人でも読むと
こんな風に書くんだっていうのの勉強になると思います。
すみません、余談でした。
JSドックを扱うルールとかに全体の時間のかなりの部分が
費やされるのは中を見たらわかります。
もう一つの興味深い点っていうのは
Lintタスク中に様々なタイミングで
ESqueryと
この記事
ちょっと僕はわからないので今回はわかりやすくAcornと呼びます。
ESqueryとAcornという二つの異なるパーサーが
動作していることになります。
この興奇心というのはJSドックのルールに時間がかかっていることに
刺激をされました。
ドックの方に意外と時間がかかっていて
画像を貼られているんですけど
バックワードトークンコメントカーサーというのがありまして
これが1.27秒かかっています。
これにこの記者の方はちょっと気になったというところですね。
ある特定のバックワードトークンコメントカーサーという項目ですけど
その中で最も大きなブロックなのでとても興味があります。
添付されたファイルの場所をソースにたどりますと
ファイルのどこにいるのかの状態を保持するクラスのようになります。
現在自分たちはファイルのどこにいるのかという状態を
保っておくためのクラスだと。
最初の対策としてそのクラスがインスタンス化されている度に
インクリメントする単純なカウンターを追加して
再びリントタスクを実行してみました。
とりあえずどれくらい時間を使っているかというよりも
どれくらいコールされているかというカウンターをセットしました。
続いてそこで次のセクションに入りますけど
2000万回迷子になったことについて
このクラスは全部で2000万回以上
インスタンスが作られていると。
これはかなり多いですよね。
インスタンス化したオブジェクトやクラスはメモリーを消費し
09:01
そのメモリーは後でクリーンアップする必要があります。
その結果ガベージコレクション、メモリーの後始末に
2.43秒かかっていることがデータで確認できます。
これはまずいですね。
作るだけ作って後で掃除すればいいけれど
その掃除するコストもかかりますし
それに2.43秒かかっているというのは本質的なところに時間をかけていないということになるので
これは確かにまずいですね。
このクラスの新しいインスタンスを作成すると
2つの関数を呼び出しますが、どちらも検索を開始するようになります。
しかし最初の関数はループを含んでいないため
何をやっているのか詳しく知ることができません。
経験上ループは通常パフォーマンスを調査するための主要な容疑者になりますので
私は通常そこから検索を開始します。
しかし2番目の
utilize.searchという関数があって
この関数はループを含んでいます。
そのutilize.search関数はその時点で
リントしていたファイルの内容から解析されたトークンのストリームをループしています。
トークンというのはプログラミング言語の最小の構成要素であり
言語の言葉であると考えることができます。
例えばJavaScriptではファンクションという単語は
通常一つのファンクショントークンとして表現され
コンマやセミコロンも同様に一つのファンクショントークンとして表現されます。
このutilize.search関数では
ファイル内の現在位置に最も近いトークンを
見つけることに関心があるようです。
ファイルの現在位置、ファイル内で今どこにいるかを見ていて
それの最も近いトークンを見つけるというのが
関心があるということですね。
関数名をもう少し分かりやすくしてほしいですね。
トークンサーチというメソッドをしてくれたら
サーチだけで言うと何をサーチしているか分かりづらいですね。
関数の定義がソースコードとして
この記事内に貼られていて
確かにファンクションサーチで
引数にはトークンズとロケーションという引数を受け取るようですね。
トークンの中からファインドインデックスで
配列になっているっぽいですね。
要素を一個一個ループで分回していて
そのロケーションとゲットスタートロケーションという
メソッドが別にあるそうです。それに書き出して
そのインデックスが一致していますかというところを見ているんですけど
それのところでインデックスを返してくれると。
一致していればそのままインデックスを返すんですけど
一致していない場合は基本的にはマイナス1を返すというので
サーチをしているということですね。
要はそのトークンズという事前に持っているトークンの配列と
自分の今の現在地と一致しているかというところですね。
現在地に最も近いトークンというのはそういう感じの見方をしている。
そういうことを内部でやっているということでした。
続けていきますと、そのために
JavaScriptのネイティブメソッドであるファインドインデックスという
メソッドがあるんですけど、これは本当に組み込み関数です。
これを使ってトークン配列の検索を行いますと。
このアルゴリズムでは次のように説明されています。
ファインドインデックス関数というのは繰り返し実行されるメソッドになります。
配列の各要素に対してインデックスの承順で
コールバックファンクションというのが真理、
12:01
ブーリアンチを示す値を返すまで与えられたコールバック関数を
一回呼び出してくれます。
ちゃんとファインドインデックスを使うときはリターンで
ブーリアンチを返してくださいということですね。
それを見つけるまでずっとコールバック関数というのはコールされて中をずっと
辿っていくということですね。
トークンの配列というのはファイル内のコード量に応じて増えていくことを考えますと
配列の全ての要素を調べるのではなくて
配列内の値を検索するということですね。
より効率的なアルゴリズムがあるはずです。
例えばその量をバイナリーサーチに置き換えれば時間は半分になります。
50%削減は良いことだと確かに思いますけど
それでもこのコードが2000万回
出されることに対処ができていません。
2000万回コールされて50%削減なので
1000万回分削減されるのは素晴らしいので
私にとってはそれこそが真の問題です。
2000万回呼び出されることそのものが真の問題だと思っていて
根本的な問題に対処するのではなく
症状の影響を軽減しようとしていることですね。
2000万回呼び出されることを
そのまま解決するのではなくて
症状の影響を影響範囲というのを軽減しようとしている。
これ自体は1回受け入れると言っているのか。
私たちはすでにファイルを反復処理しているので
現在値は正確に把握できているはずです。
しかしそれを変更するにはより進逆的な
リファクタリングが必要であり
このブロック位置で説明するには大きすぎます。
簡単には修正できないことも分かったので
他に注目すべき点があるか調べてみました。
中央の長い紫色のバーは見逃せません。
そのバーは単に色が違うだけでなく
多くの時間を費やし何百もの小さな関数を
紫色のバーは何を示しているかというと
セレクターエンジンの話かな。
続いてはセレクターエンジンの章に入ります。
さっきのJSDocの解析周りのところは
バイナリーにすら半分できるのですが
この木のヒッシャーの型のやりたいこととすると
かなり膨大な量になるのと
割とテクニカルなことをやっているということもあって
これに対しての修正のプロテクは
ドラスティックな範囲になってしまうので
今回は割愛するということです。
次はセレクターエンジンの処理が重いから
そこに注目しますという話です。
スピードスコープのコールタッグには
AS Queryと呼ばれるプロジェクトを指しています。
これは古いプロジェクトで小さなセレクター言語によって
解析されたコードから特定のオブジェクトを見つけられるようにすることが目的だそうです。
少し目を凝らすと
ドムツリー内の特定のHTML要素を探すのではなく
別のツリー構造内のオブジェクトを探すという点では
ここでも同じように機能します。
考え方は結局同じですよね。
このトレースは
NPMパッケージが最小化されたソースコードで
15:00
リリースされているということを示しています。
通常一文字である変数名の乱れというのは
このようなプロセスが行われることを強く示唆しています。
NPMパッケージにも同行されているので
代わりにそれを指すように
パッケージデータを修正してみました。
それでもう一回実行すると
次のようなデータが出てきました。
画像が一枚貼られて
ゲットパスというメソッドが使われて
このメソッドに2.35秒かかっています。
全体の6.2%なので
2000万回実行されるということもありました。
見てみますと
最小化されていないコードは最小化されたものに比べて
10から20%ほどパフォーマンスが落ちるということを
覚えておくと良いでしょう。
民主化だけでパフォーマンスが10から20%
改善しているということですね。
これは私がこれまで何度も最小化したコードと
最小化していないコードのパフォーマンスを
目安の範囲で収まっているということです。
相対的なタイミングは変わらないので
今回の調査には最適になります。
ゲットパス関数というのは少しばかり
ヘルプが必要だという話をしています。
ゲットパス関数の中身を見ていきます。
音読になって申し訳ないですが、これを見ていきます。
引き継ぎ1つ目にはオブジェクトが入っていて
引き継ぎ2つ目にはキーが入っています。
この紙をドットでスプリットして分割します。
それを変数に入れておきます。
その後何をやっているかというと
ちょっと理解に時間がかかりそうなのと
トライキャッチファイナリーで
イテレーターをやっているのですが
アンスコを使ったメソッドが呼ばれているので
これは別のところで使われている組み込み関数を使っています。
これは解析は一旦やめておきます。
オブジェクトとキーを使ってパスを取得していることだけ分かります。
記事内に戻りますと
時代遅れのトランスパイレーションが
長くつきまとうよということを言っています。
もしあなたがJavaScriptツールの世界にしばらくいたのであれば
これらの関数は非常に見覚えがあるはずです。
CreateForOfIteratorHelper
というメソッドは99.99%の確率で
このライブラリの作者ではなく
JavaScriptパイプラインによって挿入された関数であることが分かります。
これはOSSをやっている方でないとこんな話は見たことがないかもしれません。
JavaScriptにForOfのループが追加されたとき
それがあらゆる場所でサポートされるまで
結構時間がかかりました。
もともとのForInというループがあるのですが
ForOfのループは確かに時間がかかったのです。
最新のJavaScriptの機能を
ダウントランスファイルするツールは
慎重を期して非常に保守的な方法で
コードを書き出す傾向があります。
この場合文字列の配列に分割していることは分かっていますが
18:02
それを本格的なIteratorを使ってループさせるというのは
完全にやりすぎで
退屈な標準的Forループがあればそれで十分だったはずです。
しかしツールはそれを意識していないため
可能な限り多くのシナリオをカバーする関数を採用しました。
以下は比較のためのオリジナルコードになります。
ファンクション・ゲット・パスですが
別のオリジナルコードです。
さっきの関数を自分の中で勝手に書き換えています。
やっていることは結構似ているのですが
引数は一緒です。オブジェクトとキーを使っていて
最初にキーの値をドットでスプリットして分割しています。
その後をさっき見ていた
CreateForOfIteratorHelperという
メソッドを使って
シンプルなForOfを使っています。
さっきの引数の値から
分割したキーズという配列を使って
それらの要素を一個一個見ていて
オブジェクトがヌルだったらリターンオブジェクトして
ではないのであれば
分割したキーをオブジェクトのキーと一致させて
それが凸合しているものだけを返す。
音読でわかりづらいですが
単純にオブジェクトの中で
キーに含まれているものがあるかどうかを見ていなければ
基本オブジェクトをリターンしているという勝利をしています。
それでゲットパスをしているという感じです。
100万意見に近いと思うので記事を見てもらうと
分かると思います。この後この記事をシェアするので
興味のある人は見てみてください。
今の時代ForOfループは
どこでもサポートされている。
もう一度パッケージにパッチを当てて
関数の実装ソースにあるオリジナルのものと入れ替えてみました。
このたった一つの変更で約400ミリ秒の節約ができました。
すごいですね。
私はいつも無駄なポリフィルや時代遅れのダウントランスファイルに
どれだけのCPUを使っているかにすごく関心があります。
さっき自動で作られていた
クリエイトなんたらかんたらというメソッド
時代遅れというか歴史の古いものになってきて
パフォーマンスが悪いということですね。
しかしこのようなケースに出くわすとその差は歴然としていて
参考までにそのForOfループを標準のForループに置き換えて
促成してしまいました。
さっきはちなみにForOfだったんですけど
今回次に使うのは純粋なFor文ですね。
Forの中でイテレーター用の引数を持ってきますね。
forレッドiイコール0
iはkeys.lengthでmaxまでずっと分回して
i++でというお決まりの型ですね。
その書き方でも一応やってみました。
0から1つ1つ取ってきていますね。
for文でやっているので
レッドiイコール0でkey.lengthイコールを広げてやっているので
イテレーター編数0,1,2,3で増えていくというものですけど
0から何文字のところを
取ってくるようにしているという感じですね。
21:01
というようなスライスの書き方をしていますけど
ゲットパスを始めた当初は合計で2.7秒かかっていたんですけど
全ての最適化を適応した結果
480ミリ秒まで避けることができました。
純粋なゲットパスをそのまま使うのではなくて
for文にしたんですけどそこからさらに
純粋なfor文の方ですね。
古きfor文にしてみて中で使っている
スプリットメソッドを使うのではなくてスライスメソッドに書き減りました。
その辺の文を全部適応すると
486ミリ秒まで下がりました。
素晴らしいですね。
ここからさらにもう少し改善をしていくそうですけど
あともう少し区切りがいいので読みたいと思います。
続きまして
マッチーズ関数というのがあるんですけど
次はそのマッチーズ関数を見ていきたいと思います。
これを見ますと先ほど見たのと同様に
奇妙なfor文のダウントランスパイレーションによって
多くのオーバーヘッドが発生しているというのがちょっとわかりますよ。
時間を節約するために私はGitHubにアクセスをして
ソースコードから関数を直接コピーしてきました。
マッチーズというメソッドそのものを持ってきた。
マッチーズメソッドというのはトレースでさらに目立つようになったので
この変更だけで約1秒短縮になります。
マッチーズメソッドに対しても似たようなことをやったということですね。
私たちのエコシステムは多くのライブラリがこの問題に悩まされているということがわかります。
ワンクリックで全てをアップデートできる方法があればいいのに
と本当に思います。
もしかしたら逆トランスパイレーションというのが必要かもしれませんし
逆トランスパイラーとはダウントランスパイルパターンを検出して
それを再びモダンコードに変更するものですよと。
確かにそれはオーバーヘッドがかかりますよね。
逆トランスパイルする必要がそもそもないので
現代のメソッドとか関数を使って書き換えた方が良くないという話ですね。
私はJbeedという方に連絡を取って
マッチーズをさらに最適化できるかというのを確認しました。
彼の追加的な変更によって
セレクターのコード全体が変更されていない元の状態に比べて
約5倍高速されました。
彼が基本的に行ったのはそのマッチーズ関数のオーバーヘッドを取り除くことで
関連するいくつかのヘルパー関数も単純化することができました。
例えば彼はテンプレート文字列のトランスパイルが
上手くいっていないということに気づきました。
そもそも上手くいってないならプロリグ出してくださいって感じですけどね。
まずやったのは
リテラル展開していて
セレクター.バリュー.バリューを取ってきて
それをリテラルという変数にとりあえず入れていました。
それがインプットでアウトプットとしては
ダウントランスパイルスローと言ってますけど
空文字列にドットをつけてコンカットというメソッドがあります。
これはJavaScriptの文字列の組み込みメソッドがあるんですけど
コンカットってまずガチャンとやるやつですね。
このメソッドを使ってさっきのセレクター.バリュー.バリューをコンカットして
空文字列ともう一回ガチャンとくっつけていることです。
そうですね。何やってんのこれ。
24:00
これを見て
一言述べられていました。
さらに新しいセレクターをその場で関数UV出しの連鎖に
解析をしてその結果のラッパー関数をキャッシュすることで
さらにもう一歩踏み込んでいます。
このトリックによって彼はセレクターエンジンの速度をさらに大幅に
向上させることもできました。彼の変更をチェックすることを強くお勧めします。
ES Queryは今のところメンテナンスされていないようですので
プルリクをちょっとオープンしていません。
やっぱ最終的にプルリクをオープンしたのでそのプルリクを
見てみてくださいと。これが結構大きい変更だそうですね。
ES ToolsのES Queryっていうライブラリに一応
ES Lintの中で分割をしていてそこに対してのプルリクを
出していますけど
すごいですねこれは。なかなかですね。
いろんな計測した結果と背景とかをずっと書いていて
このメソッドが遅いよというので
JBという方は
長いコメントをやっているんですけど
結果的にプルリクを出して
マージされているっぽいですね。
ES Lintのバージョン1.4.1とか1.4.2にも貢献をしているということです。
というわけでそのプルリクの中身
ファイルチェンジだったら3ファイルしかないんですけど
たった3ファイルなんですけど結構長いですね。
影響範囲もやっぱり大きいし
いろんなドキュメントのところの更新をしなければいけないので
これはなかなか体力いるというのと
マッチーズメソッドを使っていたりとか
元々の組み込み関数を使われていたと。
自動的に作られていた関数のところの変更を
一個一個手続き的にやっていますね。
とても素晴らしいと思いますけど。
ES LintってテスティングフレームワークMOCAとかCHIを使っているんですけど
これを入れるためにはMOCA、CHIを
最新バージョンにするんじゃなくてバージョンの頭ですね。
ハットを取ってバージョン固定しています。じゃなきゃ動かないらしいので。
ES LintテスティングフレームワークをZESTとかに置き換えることは
多分できないんでしょうね。
全世界でこれだけ使われているし歴史も長いので
かなりドラスティックになるので結構怖いんでしょうね。
なのでバージョン固定にするという方向に走ったそうですけど
それでもちゃんと安定しているのであればいい話ですね。
すみません。だらだらと余談を言ってしまって
30分を超えてしまったので
今日の朝方は一旦ここで区切りたいと思います。
結構勉強になりましたね。そしてかなり
深いところまで読んだ結果でのブログになってきますので
これはすごく参考というか勉強になりました。
こういうことが書けるエンジニアになりたいなって
すくすく思いますね。
やっぱりそうなるにはライブラリの中まで本体まで見たりして
語れるようになるのはやっぱり一つ大事なのかなと思ったりはします。
というところで朝方はこれで以上にしたいと思います。
日曜の朝からこんな重い記事を
読売室に付き合っていただきありがとうございました。
今日はレノアさんですね。お子さんがいただきありがとうございました。
明日もこの続きを読んでいこうと思いますけど
27:00
明日も朝からちょっと重たい内容かもしれないですけど
やっぱり頑張っていきたいと思います。
じゃあ日曜日ですね。ゆっくり休んでいただいて
また明日月曜日から頑張っていきたいと思います。
火曜日は祝日になりますのでもしかしたら
20日有休言ってる方もいるかもしれないですね。
このまま4連休だっていう人もいるかもしれないですけど
僕は頑張っていきたいと思います。
じゃあ終了します。お疲れ様でした。