見出し画像

Solana Developer Hub #6: Compute Units/Budget最適化

この記事は2023/5/30に開催したSolana Developer Hub Online #6 で話をした「Compute Units/Budget」を再編集したものになります。

Compute Units/Budget

CU大好きおじさんです。CUやりはじめたら止まらなくなりますよね。

2024年に入ってからSolanaチェーンは若干詰まり気味でしたね。
そのさいにCUの話題が盛り上がっていました。

おかげさまでCUの説明もいろいろ出て知見が溜まったので、今回はCUって何?何に使われるの?どう調整すればいいの?と、CUのついて全体的に掘り下げて解説しようと思います。

まずは用語を揃えましょう。
先ほどから何度も話しているCU(Compute Unit)はProgramを実行したさいに消費するリソースになります。
そのため、CUはProgramの実装によって消費量が変わります。

算出方法については不明としてありますが、聞いたところによると基本的にはCPUのプロセッサタイムによって決定されるそうです。
ここで基本的にはとことわった理由は、他にもCUに影響を与える要素があるからです。

次にCompute Budgetです。
こちらは、クライアントから送信するトランザクションでこれだけのCUを消費する予定です。と申告する予算になります。

実際にそのトランザクションでどのくらいのCU消費が発生するかは、RPCのSimulateTransactionを実行することで推定CUを取得可能です。

このCUはだいたい2つの機能で利用されています。

  • 制限

  • 優先手数料

それぞれの詳細についてみていきましょう。

まず、実行の制限としてCUは利用されます。

  • ブロックごとの最大計算: 4,800万CU

  • ブロックごとのアカウントごとの最大計算: 1,200万CU

  • トランザクションごとの最大計算: 140万CU(デフォルト20万CU)

Solanaはブロックチェーンなので、ブロックを生成してチェーンとして連なっていく形です。
最初の2つはこのブロックに関わる制限になりますが、DApp開発者ではほとんど意識することはないかと思います。

DApp開発者が意識するのは最後のトランザクションの制限になるかと思います。

後ほど紹介しますが、トランザクションの送信のさいに何も指定しない場合はデフォルトの20万CUが制限になります。
もし、20万CUを超えて実行したいさいにはトランザクションの指定のさいにCUの申告が必要になると思ってください。

これらの制限に引っかかったトランザクションは失敗します。
このとき失敗したトランザクションにかかった手数量などは戻ってこないので、注意してください。

次に、優先手数料でCUは利用されます。

Solanaネットワークではトランザクションの送信時に手数量がかかります。
この手数量は基本手数量と優先手数料(Optional)の合算したものが手数量となります。

優先手数料はOptionalなので何も指定しない場合は0となり、基本手数料のみが手数料としてかかることになります。

この手数料の中の優先手数料の算出にCUが利用されます。
CUの大きさに合わせて優先手数料は高額になっていくため、優先手数料を利用するさいにはCU消費をどれだけ小さくできるかがユーザビリティにつながるかと思います。

このCUとBudgetを最適化して何が嬉しいの?というと、まず普段の利用では強い効果はでないかなと思います。
効果が出てくるのはSolanaチェーンが混在しているさいに効果が出てきます。

  • トランザクションが通りやすくなる

  • ユーザーの優先手数料負担が下がる

特にCUが小さければ制限にかかりづらくなり、Solanaチェーンとしてもブロックあたりのトランザクション数が向上します。
アプリケーション開発者としてはある種、間借りしているような形なので、チェーン全体の視点としても重要なポイントになってきます。

このCUは最適化して何かデメリットがあるかというと何もありません。
最適化自体もそんなに手間ではないので、とりあえずCUとCompute Budgetはきっちりやりましょう。
(ただし、CUのガチ最適化は沼です。無限に時間溶けるので初めはほどほどぐらいが良いかもしれません)

それではまずCUの最適化の方法を紹介します。

CUの最適化について調べるとSolana Developersの記事が出てきます。
そちらで紹介されているsolana-developers/cu_optimizationというリポジトリがあります。

こちらのリポジトリではcompute_fnというマクロを使い、トランザクションログに処理のCUを記録する方法を提案されています。

このマクロ内で使われているsolana_program::sol_log_compute_unitsを使うとトランザクションログにCU残高が出力されます。
似たような関数でsolana_program::sol_remaining_compute_unitsを使うと、トランザクションログではなく関数の戻り値としてCU残高を取得することができます。

どちらを使うかは趣味の領域ですが、個人的にはsol_remaining_compute_unitsを使い、その処理の範囲で消費したCUを見れた方が嬉しいかなとは思っています。

ただ、先ほど紹介した2つの関数は利用にあたって注意点があります。
この2つの関数はSolana環境下でしか動作しないため、実際にデプロイするか、solana-test-validatorを使いローカルにValidatorを立てそちらで動作させないといけません。

実際にCUを検証するにあたり、どうしたら簡単に確認できるかなと調べたところsolana-test-validatorをcargo testから起動するという方法に行きつきました。

先ほど紹介した通り、devnetやtestnet、mainnet-betaなどに実際にデプロイするのも可能ですが、結構手間が多いため、ローカルで検証しやすいsolana-test-validatorを使うのをおすすめします。

このsolana-test-validatorはRustのcrateが提供されており、その中のTestValidatorGenesisを使うとRustのコードからValidatorの立ち上げができます。

ここは完全に趣味ですが、事前にCLIでsolana-test-validatorを立ち上げてからテストを実行するというのは大嫌いなので、私はこのTestValidatorGenesisをユニットテストで立ち上げ、CUを確認する方法をとっています。
とはいえあくまでも趣味の話なので必ずそうしなければならないわけではありません。

では実際にCUを計測してみたので、処理がCUにどのように影響を与えるのかみてみましょう。


いいなと思ったら応援しよう!