Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
SlideShare a Scribd company logo
P●PをC#で
書き直したら
100倍速くなった
++C++;
岩永 信之
開幕ネタバレ
うそ、おおげさ、まぎらわしい



 PHPをC#で
 書き直したら
 100倍速くなった
言語変えただけで100倍になったら苦労しない
参考までに: 性能
 静的な言語※と動的な言語(一般論として)
  平均的には5倍程度の差(静的な言語が速い)
  文字列処理なんかだと差が出ない
  数値計算だと30倍くらい開くことも
 C#でも(時々実測する限り)
  dynamic型を使うとスピードが5分の1くらいに



言語を変えて1桁は変わるけど、2ケタはそうない
  正直、「100倍速い…」って言われて自分もビビった

              ※   コンパイル(あるいはJIT)型の言語で、
                  コンパイル時に型情報を使った最適化をかけるもの
正しくは

自分が
PHPをC#で
書き直したら
100倍速くなった
 要は、きっちり設計しなおしたから
  大まかに2ケタくらい速くなったのは本当
元P●P
とあるモジュールにて
 1つの巨大なファイル
                    参照局所性
  1クラスで約4,000行      落とす    巨大switchもJump
                            Table化されない?
  publicフィールド数50
  1つのswitch文で長さ688行、caseラベル約200個
 ログ取りや結果の出力までべた書き
  文字列連結で、固定フォーマットで
             HTMLソースコードを生成してる
             (その用途しか想定してない)
実装しなおし後C#
 クラス化
  1クラス数フィールド
  switch分岐解消(多態化)
 債務分割
  ゲーム ロジックだけ切り出し
  出力用のフォーマットは別途行う



                     (今や元より高機能だけども)
                        同様の機能の実装
                      (四角1つ1つが1クラス)
ちなみに: 書き換えの理由
 理由もなく書き替えない
  100倍程度の速度差だと動機として弱い※
 クライアント上でも同じロジック使うから
  ネットワーク切れ             チートされると
  てもプレイしたい               まずい
 クライアント上実装必須          サーバー上実装必須

  サバクラ両方で使うからC#
  ちなみに、クライアントはiOS
  サーバーもMonoにする†

           ※   サーバー代金が問題になるような負荷でもなかった
               たぶん、データベース アクセスとかの方がよっぽどネック
           †   「サーバーOSはそのままLinuxにするかも」という意味
               このモジュール以外はP●Pのまま&同一サーバー内実行
その他の効能
 整理されたことで
  開発ペースはかなり速い
   (前はバグが1か月取れない※とかあった)
 債務分割(ビューの切り離し)で
  コマンドライン デバッグ可能に
  (サーバー、クライアントはもちろん)
   企画者向けのバランス検証ツールともコード共有化
  他のゲームでも使う予定




     ※   かかる工数的に、直しても見合わないという判断あり
         バグを直すんじゃなくて、バグってるアイテムの出現確率を落として回避
今日のテーマなんだっけ?
Interaction channel
Interaction channel
その他の効能(再)
 整理されたことで
  開発ペースはかなり速い
   (前はバグが1か月取れない※とかあった)
 債務分割(ビューの切り離し)で
  コンソール デバッグ可能に
  (サーバー、クライアントはもちろん)
   企画者向けのバランス検証ツールともコード共有化
  他のゲームでも使う予定
                   これ
つまり
 こういう状態
              主要機能

                                共有


          Window                デバッグ用
  iOS                  Linux
         デスクトップ                 コマンド
クライアント                サーバー
         クライアント                  ライン


              Window
             ストア アプリ
                         今更1個増えたくらい
                         どうってことない※

              ※   ロジック担当の発言です。UI担当の方ごめんなさい。
※   ロジック担当の発言です。UI担当の方ごめんなさい。
Interaction channel
実際にやったことの片鱗を
 三択クイズ アプリ
  実際に作ったものそのものではないものの、似た仕組み
   で実装
 Windowsストア アプリでデモを
三択クイズ
  制御フロー的にはこんなの
       View           Model
                               ループ


               出題
      問題を表示           問題選択           問題集
回答             回答

               答え
      答えを表示           正誤判定
OK            次の問題へ

                      全問終了      No
                         Yes
               結果
      結果を表示           合格判定
ポイント1: Modelはいろいろ使う
    用途
•   ゲーム本体                 Model
•   サーバー上でチート検証                    ループ

•   プランナー向け調整ツール
               出題
•   デバッグはコマンドラインで         問題選択           問題集
                  回答

• iOS、Windows、Linux… 答え   正誤判定
• GUI、CUI
• 手動、自動           次の問題へ

                          全問終了      No
                             Yes
                  結果
                          合格判定
ポイント2: 完全状況再現
                乱数シード
     値を記録
                        Model
                                 ループ

• ユーザー回答を記録
               出題
• 与えた値が同じな              問題選択           問題集
  ら、
               回答
  完全に同じ実行結果
               答え
                        正誤判定
              次の問題へ
用途
                        全問終了      No
• チート検証
                           Yes
• オンライン対戦
             結果
• ゲーム途中での中断/再開          合格判定
ポイント3: View⇔Model往復
                         制御フローはModel側
                         が管理してる/すべき
        View            Model
                双方向              ループ

     制御フローの      出題
       問題を表示            問題選択           問題集
     中断と再開が必要
回答               回答

                 答え
      答えを表示             正誤判定
OK              次の問題へ

                        全問終了      No
                           Yes
                 結果
      結果を表示             合格判定
ポイント4: 非同期
               I/O処理
               タイマー処理 =非同期
        View           Model
• ユーザー操作を待ったり                   ループ
• アニメーション終了を待ったり
                出題
       問題を表示           問題選択           問題集
回答              回答

                答え
       答えを表示           正誤判定
 OK            次の問題へ

                       全問終了      No
                          Yes
                結果
       結果を表示           合格判定
ポイント まとめ
 ポイント
  ViewとModelの分離必須
  制御フローはModel側で
  完全状況再現
  ViewとModelの双方向のやりとり
  (Model側から見て)Viewは非同期


 三択クイズごときですら、たやすくない
 うまく扱えれば、いろいろなゲームに応用可能
どういうものを作ったか
Model   制御フローの中断と再開 = イテレーター

     制御フローはModel側で
                             Interaction Channel
     完全状況再現
                             間を取り持つクラス
     ViewとModelの双方向のやりとり
     (Model側から見て)Viewは非同期

 View   非同期 = Taskクラス
Model
 イテレーター化
                    Model
                             ループ


             出題
                    問題選択           問題集
             回答

             答え
                    正誤判定
            次の問題へ

                    全問終了      No
                       Yes
             結果
                    合格判定
Model
 イテレーター化
        int correctCount = 0;

        foreach (var q in _questions)
        {
            var qm = new Q(q);            問題選択
   出題       yield return qm;

            var isCorrect =
                qm.Response == q.AnswerIndex;
            if (isCorrect) correctCount++;      正誤判定
   答え       yield return new A(isCorrect);
        }

        var correctRate = correctCount / num;
 結果     yield return new Result(                合格判定
            correctRate, _acceptableRate);
Interaction Channel
  ViewとModelをつなぐ部分
         View              Model
                                    ループ


                  出題
       問題を表示               問題選択           問題集
回答                回答

                  答え
       答えを表示               正誤判定
OK               次の問題へ

                           全問終了      No
                              Yes
                  結果
       結果を表示               合格判定
Interaction Channel
   ViewとModelをつなぐ部分
                         Modelの制御フロー                        Model
                         (イテレーター)を登録
  public class Channel<TMessage>
  {
      public Channel(IEnumerator<TMessage> coroutine)

        public Task RunAsync()   実行

        public void AddAsyncHandler(Func<T, Task<TR>> handler)
                where T : Tmessage
  }


                ViewはTask非同期で応答
View
                 同期版(Func<T, TR>)もある
View
  Interaction Channelから来たメッセージを処理
      Task非同期
            View
                    出題
       問題を表示
回答                  回答

                    答え
       答えを表示
OK                 次の問題へ




                    結果
       結果を表示
View
 Interaction Channelから来たメッセージを処理
   Task非同期

 例: 問題の表示と回答(コマンドライン版)
  channel.AddHandler<Q, int>(m =>       問題文
  {
      Console.WriteLine(m.Question.Statement);

        int i = 1;                             選択肢
        foreach (var op in m.Question.Options)
            Console.WriteLine("{0}. {1}", i++, op);

        Console.Write("何番?: ");
        int answer = int.Parse(Console.ReadLine();
        return answer - 1;
  });
                            回答番号
結果
 意外と汎用性高い
  今日のデモ用の三択クイズ
  実務で、2系統のロジックをこれで実装
  結構な種類のゲームがこの仕組みに乗る
 残念なところ
  C#に型マッチング機能欲しい
    型で分岐してる
  アクター的機構も欲しい
    他の言語のアクター機能も、使いやすいとは言い難いけど
Interaction channel
Interaction channel
デモ
 コンソール版までは作ってあるんで
 じゃあ、ストア アプリ作ろうか
  今から

More Related Content

Interaction channel

  • 2. 開幕ネタバレ うそ、おおげさ、まぎらわしい PHPをC#で 書き直したら 100倍速くなった 言語変えただけで100倍になったら苦労しない
  • 3. 参考までに: 性能  静的な言語※と動的な言語(一般論として)  平均的には5倍程度の差(静的な言語が速い)  文字列処理なんかだと差が出ない  数値計算だと30倍くらい開くことも  C#でも(時々実測する限り)  dynamic型を使うとスピードが5分の1くらいに 言語を変えて1桁は変わるけど、2ケタはそうない 正直、「100倍速い…」って言われて自分もビビった ※ コンパイル(あるいはJIT)型の言語で、 コンパイル時に型情報を使った最適化をかけるもの
  • 5. 元P●P とあるモジュールにて  1つの巨大なファイル 参照局所性  1クラスで約4,000行 落とす 巨大switchもJump Table化されない?  publicフィールド数50  1つのswitch文で長さ688行、caseラベル約200個  ログ取りや結果の出力までべた書き  文字列連結で、固定フォーマットで HTMLソースコードを生成してる (その用途しか想定してない)
  • 6. 実装しなおし後C#  クラス化  1クラス数フィールド  switch分岐解消(多態化)  債務分割  ゲーム ロジックだけ切り出し  出力用のフォーマットは別途行う (今や元より高機能だけども) 同様の機能の実装 (四角1つ1つが1クラス)
  • 7. ちなみに: 書き換えの理由  理由もなく書き替えない  100倍程度の速度差だと動機として弱い※  クライアント上でも同じロジック使うから ネットワーク切れ チートされると てもプレイしたい まずい クライアント上実装必須 サーバー上実装必須  サバクラ両方で使うからC#  ちなみに、クライアントはiOS  サーバーもMonoにする† ※ サーバー代金が問題になるような負荷でもなかった たぶん、データベース アクセスとかの方がよっぽどネック † 「サーバーOSはそのままLinuxにするかも」という意味 このモジュール以外はP●Pのまま&同一サーバー内実行
  • 8. その他の効能  整理されたことで  開発ペースはかなり速い (前はバグが1か月取れない※とかあった)  債務分割(ビューの切り離し)で  コマンドライン デバッグ可能に  (サーバー、クライアントはもちろん) 企画者向けのバランス検証ツールともコード共有化  他のゲームでも使う予定 ※ かかる工数的に、直しても見合わないという判断あり バグを直すんじゃなくて、バグってるアイテムの出現確率を落として回避
  • 12. その他の効能(再)  整理されたことで  開発ペースはかなり速い (前はバグが1か月取れない※とかあった)  債務分割(ビューの切り離し)で  コンソール デバッグ可能に  (サーバー、クライアントはもちろん) 企画者向けのバランス検証ツールともコード共有化  他のゲームでも使う予定 これ
  • 13. つまり  こういう状態 主要機能 共有 Window デバッグ用 iOS Linux デスクトップ コマンド クライアント サーバー クライアント ライン Window ストア アプリ 今更1個増えたくらい どうってことない※ ※ ロジック担当の発言です。UI担当の方ごめんなさい。
  • 14. ロジック担当の発言です。UI担当の方ごめんなさい。
  • 16. 実際にやったことの片鱗を  三択クイズ アプリ  実際に作ったものそのものではないものの、似た仕組み で実装  Windowsストア アプリでデモを
  • 17. 三択クイズ  制御フロー的にはこんなの View Model ループ 出題 問題を表示 問題選択 問題集 回答 回答 答え 答えを表示 正誤判定 OK 次の問題へ 全問終了 No Yes 結果 結果を表示 合格判定
  • 18. ポイント1: Modelはいろいろ使う 用途 • ゲーム本体 Model • サーバー上でチート検証 ループ • プランナー向け調整ツール 出題 • デバッグはコマンドラインで 問題選択 問題集 回答 • iOS、Windows、Linux… 答え 正誤判定 • GUI、CUI • 手動、自動 次の問題へ 全問終了 No Yes 結果 合格判定
  • 19. ポイント2: 完全状況再現 乱数シード 値を記録 Model ループ • ユーザー回答を記録 出題 • 与えた値が同じな 問題選択 問題集 ら、 回答 完全に同じ実行結果 答え 正誤判定 次の問題へ 用途 全問終了 No • チート検証 Yes • オンライン対戦 結果 • ゲーム途中での中断/再開 合格判定
  • 20. ポイント3: View⇔Model往復 制御フローはModel側 が管理してる/すべき View Model 双方向 ループ 制御フローの 出題 問題を表示 問題選択 問題集 中断と再開が必要 回答 回答 答え 答えを表示 正誤判定 OK 次の問題へ 全問終了 No Yes 結果 結果を表示 合格判定
  • 21. ポイント4: 非同期 I/O処理 タイマー処理 =非同期 View Model • ユーザー操作を待ったり ループ • アニメーション終了を待ったり 出題 問題を表示 問題選択 問題集 回答 回答 答え 答えを表示 正誤判定 OK 次の問題へ 全問終了 No Yes 結果 結果を表示 合格判定
  • 22. ポイント まとめ  ポイント  ViewとModelの分離必須  制御フローはModel側で  完全状況再現  ViewとModelの双方向のやりとり  (Model側から見て)Viewは非同期  三択クイズごときですら、たやすくない  うまく扱えれば、いろいろなゲームに応用可能
  • 23. どういうものを作ったか Model 制御フローの中断と再開 = イテレーター  制御フローはModel側で Interaction Channel  完全状況再現 間を取り持つクラス  ViewとModelの双方向のやりとり  (Model側から見て)Viewは非同期 View 非同期 = Taskクラス
  • 24. Model  イテレーター化 Model ループ 出題 問題選択 問題集 回答 答え 正誤判定 次の問題へ 全問終了 No Yes 結果 合格判定
  • 25. Model  イテレーター化 int correctCount = 0; foreach (var q in _questions) { var qm = new Q(q); 問題選択 出題 yield return qm; var isCorrect = qm.Response == q.AnswerIndex; if (isCorrect) correctCount++; 正誤判定 答え yield return new A(isCorrect); } var correctRate = correctCount / num; 結果 yield return new Result( 合格判定 correctRate, _acceptableRate);
  • 26. Interaction Channel  ViewとModelをつなぐ部分 View Model ループ 出題 問題を表示 問題選択 問題集 回答 回答 答え 答えを表示 正誤判定 OK 次の問題へ 全問終了 No Yes 結果 結果を表示 合格判定
  • 27. Interaction Channel  ViewとModelをつなぐ部分 Modelの制御フロー Model (イテレーター)を登録 public class Channel<TMessage> { public Channel(IEnumerator<TMessage> coroutine) public Task RunAsync() 実行 public void AddAsyncHandler(Func<T, Task<TR>> handler) where T : Tmessage } ViewはTask非同期で応答 View 同期版(Func<T, TR>)もある
  • 28. View  Interaction Channelから来たメッセージを処理  Task非同期 View 出題 問題を表示 回答 回答 答え 答えを表示 OK 次の問題へ 結果 結果を表示
  • 29. View  Interaction Channelから来たメッセージを処理  Task非同期 例: 問題の表示と回答(コマンドライン版) channel.AddHandler<Q, int>(m => 問題文 { Console.WriteLine(m.Question.Statement); int i = 1; 選択肢 foreach (var op in m.Question.Options) Console.WriteLine("{0}. {1}", i++, op); Console.Write("何番?: "); int answer = int.Parse(Console.ReadLine(); return answer - 1; }); 回答番号
  • 30. 結果  意外と汎用性高い  今日のデモ用の三択クイズ  実務で、2系統のロジックをこれで実装  結構な種類のゲームがこの仕組みに乗る  残念なところ  C#に型マッチング機能欲しい  型で分岐してる  アクター的機構も欲しい  他の言語のアクター機能も、使いやすいとは言い難いけど