0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

パフォチューってなに?美味しいの?なエンジニアが、AIでUnityパフォーマンス改善に挑戦した話(静的解析編)

Last updated at Posted at 2025-12-24

本記事は サムザップ Advent Calendar 202525日目 の記事です 🎄

はじめに:パフォチューってなに?美味しいの?🍰

パフォーマンスチューニング?
正直、「できる人がやるやつ」だと思ってました。

自分は Unity のクライアントエンジニアで、
機能開発や不具合修正はそれなりにやってきましたが、

  • Profiler は開いたことあるだけ
  • 数字を見ても「で、何を直すの?」状態
  • パフォーマンス改善は 完全に未経験

そんな自分が最近知ったのが、

パフォーマンス改善には
静的解析(コードを見る)
動的解析(実行時を見る) の2つがある

という事実。

いきなり Profiler とにらめっこするのはハードルが高い。
じゃあまずは 静的解析からやってみよう

そして思いました。

AIがいる今なら、
パフォチュー赤ちゃんでもいけるんじゃない?

というわけで、
AIと一緒に Unity 向けの静的解析ツールを育ててみることにしました。


静的解析、AIに頼ってみた(Cursorさん、お願いします🙏)

最初に考えたのはこんな感じです。

  • 手でコードレビュー?
    → 無理。量が多すぎる
  • 既存の静的解析ツール?
    → 厳しすぎる or Unityの文脈を見てくれない

そこで頼ったのが Cursor

Cursorさんにお願いしたことはシンプルで、

  • Update / LateUpdate / FixedUpdate を見る
  • 毎フレーム重い処理を検知する
  • ただし うるさすぎないことが最重要

…だったのですが。


でも最初は地獄だった(誤検知がウザすぎる😇)

最初にできたツールは、
とにかく正義感が強い

例えば、こんな警告が出ました。

{
  "rule": "UPD_TRS_LOOP",
  "severity": "S",
  "message": "Loop contains heavy operation: TransformPoint. This is executed every frame."
}

一見すると、正しそうです。
でも対象コードを見ると…

private void LateUpdate()
{
    if (_commonSpineController == null) return;

    // Bone追従オブジェクトの位置更新
    // SpineMotionMixerBehaviourでのボーン更新と1フレずれないようにLateUpdateで更新している
    foreach (var (boneFollower, targetBone) in _boneFollowers)
    {
        var boneWorldPosition =
            _commonSpineController.transform.TransformPoint(
                new Vector3(targetBone.WorldX, targetBone.WorldY, 0));
        boneFollower.position = boneWorldPosition;
    }

    // Bone初期位置追従オブジェクトの位置更新
    foreach (var boneInitialFollower in _boneInitialFollowers)
    {
        var boneWorldInitialPosition =
            _commonSpineController.transform.TransformPoint(
                boneInitialFollower.initialPosition);
        boneInitialFollower.boneFollower.position = boneWorldInitialPosition;
    }
}

「意図して書いたコード」に怒られる問題

これ、どう見ても 意図的 な処理です。

  • Spine のボーン追従
  • フレームずれを防ぐために LateUpdate
  • TransformPoint を使うのも分かった上でやっている

それなのに、毎回 severity: S で怒られる。

正直な感想はこれでした。

これ、実プロジェクトで使ったら
「うるさいだけのツール」になるな…


発想を変えた:「怒られないツール」を作ろう

ここで方針を切り替えました。

AIに 「全部判断させる」 のをやめて、
人間の意図を前提にする設計 にします。

具体的には:

  • 意図的な処理はコメントで宣言できるようにする
  • Update 内でも
    • 早期 return ガードがある
    • 明らかに軽いループ
    • Editor 専用コード
      severity を下げる or スキップ
  • 「完璧な検知」よりも
    レビューで納得できる精度を目指す

Cursorさんとコンテキストを詰めながら、

  • 「それは怒らなくていい」
  • 「これはさすがに重い」

を少しずつ教えていきました。


改善後の具体例:「これは確かに指摘してほしい」

誤検知が減った一方で、
「これはちゃんと怒られていい」ケースも残るようになりました。

たとえば、次のような検知です。

{
  "rule": "UPD_TRS_LOOP",
  "severity": "S",
  "file": "Assets/Project/Battle2/Scripts/Main/View/Unit/UnitEffect.cs",
  "method": "LateUpdate",
  "line": 116,
  "loop_line": 116,
  "loop_type": "foreach",
  "message": "Loop contains heavy operation: TransformPoint. This is executed every frame.",
  "snippet": "foreach (var boneInitialFollower in _boneInitialFollowers)\n            { // TransformPoint"
}

対象になったコードはこちらです。

private void LateUpdate()
{
    if (_commonSpineController == null) return;

    // Bone追従オブジェクトの位置更新
    // SpineMotionMixerBehaviourでのボーン更新と1フレずれないようにLateUpdateで更新している
    foreach (var (boneFollower, targetBone) in _boneFollowers)
    {
        var boneWorldPosition =
            _commonSpineController.transform.TransformPoint(
                new Vector3(targetBone.WorldX, targetBone.WorldY, 0));
        boneFollower.position = boneWorldPosition;
    }

    // Bone初期位置追従オブジェクトの位置更新
    foreach (var boneInitialFollower in _boneInitialFollowers)
    {
        var boneWorldInitialPosition =
            _commonSpineController.transform.TransformPoint(
                boneInitialFollower.initialPosition);
        boneInitialFollower.boneFollower.position = boneWorldInitialPosition;
    }
}

ここが 「あー、これは確かに指摘されていいな」 と思えたポイントです。

  • 処理自体は 意図的
  • LateUpdate を使っている理由も明確
  • ただし
    • 2つのループ
    • 毎フレーム
    • TransformPoint を複数回実行
      しているのは事実

つまりこれは、

「設計として正しいけど、
本当にこの頻度・この実装で最適かは
一度立ち止まって考えたい」

タイプのコード。

即バグではないけど、
パフォーマンス改善の余地はある。

Severity: S が付くのも、
今ならちゃんと納得できます。

この時点で初めて、

  • 「怒られた」ではなく
  • 「レビューされた」

という感覚になりました。

改善後:ちゃんと“会話できる”結果になった

最終的に出てきた結果はこんな感じです。

"stats": {
  "by_severity": {
    "S": 3,
    "A": 7,
    "C": 5
  }
}
  • 意図的な処理 → 出ない or 軽め(C)
  • 本当に危ない処理 → Sで出る
  • 全体量も 「ちゃんと読める数」

これなら、

  • CIで落とさなくてもいい
  • レビューで「ここどうする?」と会話できる
  • パフォーマンス未経験者でも怖くない

「怒られない静的解析ツール」 になりました。


ここまでできた(パフォチュー初心者でも)

自分は、

  • パフォーマンス未経験
  • Profilerも怖い
  • パフォチューって何?状態

それでも、

  • 実プロジェクトで使える静的解析ツールを作れた
  • 誤検知を減らし、意図を伝えられる設計にできた
  • AIを「魔法」ではなく「相棒」として使えた

というところまでは、ちゃんと来れました。


次は動的解析(AI × Unity Profiler)

この記事は 静的解析編 です。

次にやりたいのは、

  • Unity Profiler の結果をAIに読ませる
  • 「どこが重いか」を言語化させる
  • 静的解析と組み合わせて改善ポイントを絞る

まだ試行中ですが、
これもまた パフォチュー初心者向けのやり方 で進める予定です。


おわりに

パフォーマンスチューニングは、
「できる人だけのもの」じゃありませんでした。

AIがいる今なら、

  • 分からなくても
  • 怖くても
  • 少しずつ教えながら

一緒に前に進める。

パフォチュー、
意外と美味しいかもしれません。🍰

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?