Mercurial > hg > Papers > 2022 > riono-master
view Slide/master-slide.html @ 57:309de2ffb2bd
update
author | riono <e165729@ie.u-ryukyu.ac.jp> |
---|---|
date | Mon, 14 Feb 2022 20:57:33 +0900 |
parents | 01087d212c1a |
children | c269b11bb810 |
line wrap: on
line source
<!DOCTYPE html> <html> <head> <meta http-equiv="content-type" content="text/html;charset=utf-8"> <title>継続を使用する並列分散フレームワークのUnity実装</title> <meta name="generator" content="Slide Show (S9) v4.1.0 on Ruby 2.6.3 (2019-04-16) [universal.x86_64-darwin19]"> <meta name="author" content="Ryo Yasuda, Shinji Kono" > <!-- style sheet links --> <link rel="stylesheet" href="s6/themes/projection.css" media="screen,projection"> <link rel="stylesheet" href="s6/themes/screen.css" media="screen"> <link rel="stylesheet" href="s6/themes/print.css" media="print"> <link rel="stylesheet" href="s6/themes/blank.css" media="screen,projection"> <!-- JS --> <script src="s6/js/jquery-1.11.3.min.js"></script> <script src="s6/js/jquery.slideshow.js"></script> <script src="s6/js/jquery.slideshow.counter.js"></script> <script src="s6/js/jquery.slideshow.controls.js"></script> <script src="s6/js/jquery.slideshow.footer.js"></script> <script src="s6/js/jquery.slideshow.autoplay.js"></script> <!-- prettify --> <link rel="stylesheet" href="scripts/prettify.css"> <script src="scripts/prettify.js"></script> <script> $(document).ready( function() { Slideshow.init(); $('code').each(function(_, el) { if (!el.classList.contains('noprettyprint')) { el.classList.add('prettyprint'); } }); prettyPrint(); } ); </script> <!-- Better Browser Banner for Microsoft Internet Explorer (IE) --> <!--[if IE]> <script src="s6/js/jquery.microsoft.js"></script> <![endif]--> </head> <body> <div class="layout"> <div id="header"></div> <div id="footer"> <div align="right"> <img src="s6/images/logo.svg" width="200px"> </div> </div> </div> <div class="presentation"> <div class='slide cover'> <table width="90%" height="90%" border="0" align="center"> <tr> <td> <div align="center"> <h1><font color="#808db5">継続を使用する並列分散フレームワークのUnity実装</font></h1> </div> </td> </tr> <tr> <td> <div align="left"> Ryo Yasuda, Shinji Kono 並列信頼研 <hr style="color:#ffcc00;background-color:#ffcc00;text-align:left;border:none;width:100%;height:0.2em;"> </div> </td> </tr> </table> </div> <div class='slide'> <h3 id="本研究における成果">本研究における成果</h3> <ul> <li>並列分散フレームワークChristieをUnityで動作可能に <ul> <li>Unity上でp2pを基礎としたオンラインゲームの開発が可能に</li> </ul> </li> <li>Christie Sharpと既存のUnityの通信フレームワークとの機能的な比較を行った <ul> <li>Christie Sharpの利点 <ul> <li>単体でも並列ライブラリとして機能する</li> <li>通信切断が起こった場合でもゲームロジックが停止しない</li> </ul> </li> </ul> </li> </ul> </div> <div class='slide'> <!-- _S9SLIDE_ --> <h3 id="概要">概要</h3> <ul> <li>オンラインゲームにおける通信にはクライアントサーバ方式が主流 <ul> <li>データの共有はサーバを経由するため低速</li> </ul> </li> <li>当研究室で開発を行っているChristie の分散計算を使用することで、高速に通信できると考えた</li> <li>Christie をUnity で使用するためにC# で書き換えを行った</li> <li>Christie SharpとUnity既存の通信フレームワークとの機能的な比較を行った</li> </ul> </div> <div class='slide'> <!-- _S9SLIDE_ --> <h3 id="オンラインゲームにおけるデータ通信">オンラインゲームにおけるデータ通信</h3> <ul> <li>オンラインゲームは複数のプレイヤーが関与する分散プログラム <ul> <li>分散プログラムを正しく書くことは難しい <ul> <li>ネットワークの変化、故障、性能の多様性を吸収する</li> <li>スケーラビリティー リソースの追加のみでサービスの質の直感的に維持できる性能基準を備える</li> </ul> </li> <li>Debugも困難な場合が多い</li> </ul> </li> <li>クライアントの負荷軽減やチート対策のため、クライアントサーバ方式が主流 <ul> <li>データの同期にはサーバを経由するためp2pに比べ低速</li> </ul> </li> </ul> </div> <div class='slide'> <!-- _S9SLIDE_ --> <h3 id="オンラインゲームにおけるデータ通信-1">オンラインゲームにおけるデータ通信</h3> <ul> <li>当研究室では並列分散通信フレームワークChristie を開発中である <ul> <li>型のあるDataGear とKey を持つタプル空間、DataGearManager として格納している</li> <li>他のノードはDGM のproxyを持っており、proxy に書き込むことで通信を実現している</li> <li>DGM はトポロジーマネージャーによって自動的に構築される <ul> <li>プログラム自体はDGM の名前を知っていれば良い</li> <li>他のノードのIP addressなどを知る必要はない</li> </ul> </li> </ul> </li> <li>ネットワークが切断されてもゲームは継続可能</li> <li>本研究ではChristieをC#で再実装を行い、Unityの通信ライブラリとして使用可能にする</li> <li>既存のUnityの通信ライブラリとの機能面での比較を行い、Christie Sharpのオンラインゲーム向けの通信ライブラリとしての考察を行う</li> </ul> </div> <div class='slide'> <!-- _S9SLIDE_ --> <h3 id="christie-の基礎概念">Christie の基礎概念</h3> <ul> <li>Christie は当研究室で開発をしている並列分散通信フレームワークである <ul> <li>同じく当研究室で開発しているGearsOS に導入予定のため次のような概念を持っている</li> </ul> </li> <li>CodeGear (クラスやスレッド)</li> <li>DataGear (変数データ)</li> <li>CodeGearManager (CG,DG,DGMを管理)</li> <li>DataGearManager (DGを管理,localとremoteの2種類がある, put操作によりDGを格納)</li> </ul> <center><img src="../Paper/images/GearsRelationships.pdf" alt="message" width="550" height="350" /></center> <center> 各Gearの関係性 </center> </div> <div class='slide'> <!-- _S9SLIDE_ --> <h3 id="christie-の基礎概念-1">Christie の基礎概念</h3> <ul> <li>全てのCGM はThreadPool と他のCGM をList として共有している</li> <li>ThreadPool はCPU に合わせた並列度でqueue に入ったThread を逐次実行していく <ul> <li>1つのThreadPool で処理を行うことでCPU のコア数に適したThread を管理でき、並列度を下げ流ことを防ぐ</li> </ul> </li> <li>ThreadPoolを共有することメタレベルで全てのCG/DG にアクセス可能</li> </ul> <center><img src="../Paper/images/ChristieClass.pdf" alt="message" width="600" height="450" /></center> <center> Christie を同一プロセスで複数インスタンス立ち上げた際の接続の構造図 </center> </div> <div class='slide'> <!-- _S9SLIDE_ --> <h3 id="christie-の基礎概念-annotationについて">Christie の基礎概念 annotationについて</h3> <p>DG を取り出すためにCG内に宣言した変数データにannotation をつける。annotationには以下の4つがある。</p> <ul> <li>Take <ul> <li>先頭のDG を読み込み、そのDG を削除する</li> <li>DG が複数ある場合Take を使用する</li> </ul> </li> <li>Peek <ul> <li>先頭のDG を読み込むがDG を削除しない</li> <li>操作をしない場合は同じデータを参照し続ける</li> </ul> </li> <li>TakeFrom <ul> <li>Take と同じ動作だが、remote 先のDGMを指定できる</li> </ul> </li> <li>PeekFrom <ul> <li>Peek と同じ動作だが、remote 先のDGMを指定できる</li> </ul> </li> </ul> </div> <div class='slide'> <!-- _S9SLIDE_ --> <h3 id="topology-manager">Topology Manager</h3> <ul> <li>Christie 上でNetwork Topology を形成する <ul> <li>参加を表明したノードに名前を与える</li> <li>必要があればノード同士の配線を自動で行う</li> </ul> </li> <li>静的Topology と動的Topology 2種類がある</li> </ul> </div> <div class='slide'> <!-- _S9SLIDE_ --> <h3 id="topology-manager静的topology">Topology Manager 静的Topology</h3> <ul> <li>静的Topology は以下のようなdot ファイルを与えることでNode の関係を構築できる</li> <li>それぞれのNode への通信にはIP address などは使用せずright というlabel を使用することで接続できる</li> </ul> <center>dot 形式による node 間接続の記述</center> <pre><code class="language-ring.dot">digraph test { node0 -> node1 [label="right"] node1 -> node2 [label="right"] node2 -> node0 [label="right"] } </code></pre> <center><img src="../Paper/images/ring.pdf" alt="message" width="480" height="300" /></center> <center> ring状の接続 </center> </div> <div class='slide'> <!-- _S9SLIDE_ --> <h3 id="topology-manager静的topology-1">Topology Manager 静的Topology</h3> <ul> <li>動的Topologyは以下の図のように自動的に接続される</li> <li>現在はTree型の接続のみに対応している</li> </ul> <center><img src="../Paper/images/DynamicTopology.pdf" alt="message" width="1000" height="450" /></center> <center>動的Topologyの接続手順</center> </div> <div class='slide'> <!-- _S9SLIDE_ --> <h3 id="christie-のコード例">Christie のコード例</h3> <ul> <li>コンストラクタで与えた数字をインクリメントし、10回printする例題 <pre><code class="language-java:StartCountup.java">public class StartCountup extends StartCodeGear { public StartCountup(CodeGearManager cgm) { super(cgm); } public static void main(String args[]) { StartCountup start = StartCountup(createCGM(10001)); } @Override protected void run(CodeGearManager cgm) { cgm.setup(new CountUpper()); CountObject count = new CountObject(1); put("count", count); } } </code></pre> <p>```java:CountUpper.java public class CountUpper extends CodeGear { @Take public CountObject count;</p> <p>@Override protected void run(CodeGearManager cgm) { System.out.println(count);</p> <pre><code> if (count < 10) { cgm.setup(new CountUpper()); count.number += 1; put("count", count); } else { cgm.getLocalDGM().finish(); } } } </code></pre> </li> </ul> <pre><code>```java:CountObject.java @Message public class CountObject { public int number; public CountObject(int number) { this.number = number; } } </code></pre> </div> <div class='slide'> <!-- _S9SLIDE_ --> <h3 id="christie-のコード例-1">Christie のコード例</h3> <p>シーケンス図を使った説明</p> </div> <div class='slide'> <!-- _S9SLIDE_ --> <h3 id="cへの書き換えの意義">C#への書き換えの意義</h3> <ul> <li>ChristieはJavaで実装されている <ul> <li>C#に書き換えを行うべきか</li> </ul> </li> <li>Unityはandroidの開発向けにjarファイルからJavaのメソッドをC#上で呼び出せる機能がある <ul> <li>stringを使用してリソースディレクトリから検索し、使用</li> <li>高速化が求められる並列分散プログラミングには不適</li> </ul> </li> <li>JavaとC#の2つの管理が必要になる <ul> <li>C#とJavaは記述方法が非常に似ており、APIもほとんど同様な機能が実装されている</li> <li>リポジトリをネスト化して対応</li> </ul> </li> </ul> </div> <div class='slide'> <!-- _S9SLIDE_ --> <h3 id="書き換えの方針">書き換えの方針</h3> <ul> <li>C#で記述するChristieをChristie Sharpとする</li> <li>Christie設計時の意図や、互換性を保つためChristieと同じ動作をさせる</li> <li>ChristieはJava9から開発されており、非推奨なコードやが含まれている</li> <li>C#に対応しつつ、処理動作の向上や最適化を行うために以下の変更を行った <ul> <li>MessagePackのバージョンアップ</li> <li>ThreadPoolからTaskへ変更</li> </ul> </li> </ul> </div> <div class='slide'> <!-- _S9SLIDE_ --> <h3 id="java-からの変更点">Java からの変更点</h3> <ul> <li>Java とC# は基本的に書き方は変わらない</li> </ul> <pre><code class="language-java:ex.java">Java public class StartCountup extends StartCodeGear { } @Override protected void run(CodeGearManager cgm) { } @Take String countObject; </code></pre> <pre><code class="language-cs:ex.cs">C# public class StartCountup : StartCodeGear { } public override void Run(CodeGearManager cgm) { } [Take] string countObject; </code></pre> </div> <div class='slide'> <!-- _S9SLIDE_ --> <h3 id="christie-sharp-のコード例">Christie Sharp のコード例</h3> <pre><code class="language-cs:StartCountUp.cs">public class StartCountUp : StartCodeGear { public StartCountUp(CodeGearManager cgm) : base(cgm) { } public static void Main(string[] args) { StartCountUp start = StartCountUp(createCGM(10001)); } public override void Run(CodeGearManager cgm) { cgm.Setup(new CountUpper()); CountObject count = new CountObject(1); put("count", count); } } </code></pre> <pre><code class="language-cs:CountUpper.cs">public class CountUpper : CodeGear { [Take] public CountObject count; public override void Run(CodeGearManager cgm) { Console.WriteLine(count.number); if (count.number < 10) { cgm.Setup(new CountUpper()); count.number += 1; put("count", count); } else { cgm.GetLocalDGM().Finish(); } } } </code></pre> <pre><code class="language-cs:CountObject.cs">[MessagePackObject] public class CountObject { public int number; public CountObject(int number) { this.number = number; } } </code></pre> <!-- 1. Main関数でCGM のインスタンス生成 2. 2つのCG をsetupして待ち状態にする 3. key:hellowWorld data:"hello" がTake される 4. 変数が揃ったためStartHelloWorld のRun が実行される 5. "hello" がprintされ、再び待ち状態になる。 key:hellow data:"hello"がput される 6. key:hellowWorld data:"world" がTake され、4,5と同様に処理される 7. 変数hello とworld がput され揃ったため、FinishHelloWorld のRun が実行され、プログラムは終了する --> </div> <div class='slide'> <!-- _S9SLIDE_ --> <h3 id="take-annotationの実装">Take annotationの実装</h3> <ul> <li>Christie ではDGを取得するためにannotation を使用している <ul> <li>C# ではannotation と同様の機能にattribute があり、Take をattribute で実装した</li> </ul> </li> <li>Take はフィールド変数に対して適用する</li> </ul> <pre><code class="language-java:Take.java">@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface Take { } </code></pre> <pre><code class="language-cs:Take.cs">[AttributeUsage(AttributeTargets.Field)] public class Take : Attribute { } </code></pre> </div> <div class='slide'> <!-- _S9SLIDE_ --> <h3 id="messagepackの相違点">MessagePackの相違点</h3> <ul> <li>Christie ではMessagePack を使用してデータを圧縮し送受信している <ul> <li>インスタンス内のpublic 変数に対して圧縮可能</li> </ul> </li> <li>バージョンが古いため、現在はサポートされていない <ul> <li>そのため、最新版とは記述方法が異なる</li> </ul> </li> <li>圧縮するクラスには@Message annotatoinをつける</li> <li>MessagePack インスタンスを作成後、write、read することでデータの圧縮解凍が可能 <ul> <li>圧縮されたデータはbyte[] 型になる</li> </ul> </li> </ul> <pre><code class="language-java:MessagePackEx.java">public class MessagePackExample { @Message public static class MyMessage { public String name; public double version; } public static void main(String[] args) throws Exception { MyMessage src = new MyMessage(); src.name = "msgpack"; src.version = 0.6; MessagePack msgpack = new MessagePack(); // Serialize byte[] bytes = msgpack.write(src); // Deserialize MyMessage dst = msgpack.read(bytes, MyMessage.class); } } </code></pre> </div> <div class='slide'> <!-- _S9SLIDE_ --> <h3 id="messagepackの相違点-1">MessagePackの相違点</h3> <ul> <li>C# のMessagePack は複数存在している <ul> <li>java 版と似たような書き方をするMessagePack-CSharp を選択した</li> </ul> </li> <li>圧縮を行いたいクラスに対してMessagePackObject attribute を付ける</li> <li>圧縮する変数に対してkey を設定できる <ul> <li>解凍時にjson として展開できる</li> </ul> </li> <li>データの圧縮にはMessagePackSerializer.Serialize 関数を用い、byte[] に圧縮される</li> <li>データの解凍にはMessagePackSerializer.Deserialize 関数を使用する <ul> <li>Deserialize 関数はジェネリスク関数であるため<>内に解凍するデータの型情報を記述する</li> </ul> </li> </ul> <pre><code class="language-cs.MessagePackEx.cs">[MessagePackObject] public class MyClass { [Key(0)] public int Age { get; set; } [Key(1)] public string FirstName { get; set; } [Key(2)] public string LastName { get; set; } static void Main(string[] args) { var mc = new MyClass { Age = 99, FirstName = "hoge", LastName = "huga", }; byte[] bytes = MessagePackSerializer.Serialize(mc); MyClass mc2 = MessagePackSerializer.Deserialize<MyClass>(bytes); // [99,"hoge","huga"] var json = MessagePackSerializer.ConvertToJson(bytes); Console.WriteLine(json); } } </code></pre> </div> <div class='slide'> <!-- _S9SLIDE_ --> <h3 id="threadpoolからtaskへの書き換え">ThreadPoolからTaskへの書き換え</h3> <ul> <li>Christie ではThreadPool を使用していた <ul> <li>Christie # ではThreadPoolより高機能なTask を用いて書き換えを行った</li> </ul> </li> <li>Task は複雑な非同期処理を通常のコーディングと同じ感覚で直感的に記述できる</li> <li>裏でThreadPool が動くようになっている <ul> <li>大きく動作は変わらない</li> </ul> </li> </ul> </div> <div class='slide'> <!-- _S9SLIDE_ --> <h3 id="threadpoolからtaskへの書き換え-1">ThreadPoolからTaskへの書き換え</h3> <pre><code class="language-java:PriorityThreadPoolExecutors.java">public class PriorityThreadPoolExecutors { private static class PriorityThreadPoolExecutor extends ThreadPoolExecutor { private static final int DEFAULT_PRIORITY = 0; private static AtomicLong instanceCounter = new AtomicLong(); public PriorityThreadPoolExecutor(int corePoolSize, int maximumPoolSize, int keepAliveTime, TimeUnit unit) { super(corePoolSize, maximumPoolSize, keepAliveTime, unit, (BlockingQueue) new PriorityBlockingQueue<ComparableTask>(10, ComparableTask.comparatorByPriorityAndSequentialOrder())); } @Override public void execute(Runnable command) { super.execute(command); } } } </code></pre> <pre><code class="language-cs:ThreadPoolExecuters.cs">public class ThreadPoolExecutors { public ThreadPoolExecutors(int nWorkerThreads, int nIOThreads) { ThreadPool.SetMinThreads(nWorkerThreads, nIOThreads); } public void Execute(CodeGearExecutor command) { Task.Factory.StartNew(() => command.Run()); } } </code></pre> </div> <div class='slide'> <!-- _S9SLIDE_ --> <h3 id="unity">Unity</h3> <ul> <li>UnityはUnity Technologies が開発を行っているゲームエンジンである <ul> <li>世界中で使用されているゲームエンジン</li> <li>非常に軽く、スペックが低いノートPCでもゲーム開発が可能</li> </ul> </li> <li>プログラミング言語にはC# が採用されている <ul> <li>C# のAPI やUnity 向けに拡張されたAPIも使用可能</li> <li>開発した機能をUnity に組み込むことも可能</li> </ul> </li> </ul> </div> <div class='slide'> <!-- _S9SLIDE_ --> <h3 id="christie--on-unityのコード例">Christie # on Unityのコード例</h3> <pre><code class="language-cs:TransformMoveTest.cs">public class TransformMoveTest : MonoBehaviour { private CodeGearManager cgm; public Transform otherTransform; private Vector3 pos; void Start() { cgm = StartCodeGear.CreateCgm(10000); cgm.Setup(new PositionAssignCodeGear()); cgm.GetLocalDGM().Put("transform", transform); } public void Update() { pos = otherTransform.position; Vector3 newPos = new Vector3(pos.x + 3, pos.y, pos.z + 3); cgm.GetLocalDGM().Put("pos", newPos); } private void LateUpdate() { cgm.Setup(new PositionAssignCodeGear()); } } </code></pre> <pre><code class="language-cs:PositionAssingCodeGear.cs">public class PositionAssignCodeGear : CodeGear { [Take] private Vector3 pos; [Peek] private Transform transform; public override void Run(CodeGearManager cgm) { MainThreadDispatcher.Post(_ => { transform.position = pos; }, null); } } </code></pre> </div> <div class='slide'> <!-- _S9SLIDE_ --> <h3 id="unityで使用されているライブラリとの比較">Unityで使用されているライブラリとの比較</h3> <p>Unityで使用されている既存のライブラリとして、Photon Unity Networking 2(PUN2)、Mirrorと、Christie # の比較を行う。</p> <table> <thead> <tr> <th> </th> <th>Christie #</th> <th>PUN2</th> <th>Mirror</th> </tr> </thead> <tbody> <tr> <td>通信方式</td> <td>p2p</td> <td>クライアントサーバ方式</td> <td>クライアントサーバ方式</td> </tr> <tr> <td>プロトコル</td> <td>TCP</td> <td>TCP</td> <td>TCP</td> </tr> <tr> <td>特徴</td> <td>通信のためのIP address がプログラム直接記述されていない</td> <td>Photon Cloud でサーバを自前で用意する必要がない</td> <td>Unity公式でサポートされている RPC が使用可能</td> </tr> </tbody> </table> </div> <div class='slide'> <!-- _S9SLIDE_ --> <h3 id="christie-sharpの利点">Christie Sharpの利点</h3> <ul> <li>単体でも並列処理が可能 <ul> <li>CodeGear/DataGearを利用した待ち合わせ処理</li> <li>CodeGearの処理はTaskによってThreadPoolで行われる</li> <li>外部のライブラリを使用しなくてもUnityで強力な並列処理が可能</li> </ul> </li> <li>通信が切断した際にゲームロジックが停止しない <ul> <li>通信先の参照データをPeekで取得する</li> <li>DGMでproxyとしてデータはやりとりされているため、データは更新されないが参照は可能</li> <li>TopologyManagerにCookieの機能があるため、改良すれば切断から復帰可能であると考える</li> </ul> </li> </ul> </div> <div class='slide'> <!-- _S9SLIDE_ --> <h3 id="まとめ">まとめ</h3> <ul> <li>Christie をUnity で使用するためにC# に書き換えを行った</li> <li>書き換え方針としては、attribute やMessagePack などC# 独自の機能に対応しつつ元のソースコードと同じ機能になるようにした</li> <li>Unityで動作検証を行い、正しく動作することを確認した</li> <li>Unity既存のライブラリとの比較を行った</li> </ul> <h3 id="今後の課題">今後の課題</h3> <ul> <li>TopologyManagerの完成 <ul> <li>2nd keyを用いたTreeMapの通信</li> </ul> </li> <li>Christie の性能検証を行う</li> </ul> </div> </div><!-- presentation --> </body> </html>