SlideShare a Scribd company logo
1 of 55
Download to read offline
C#JobSystem を使った
Unity流マルチスレッドプログラミング
ユニティ・テクノロジーズ・ジャパン合同会社
エバンジェリスト

伊藤 周
• 今回紹介するC# Job Systemはまだ発展段階
• リリースでは多少の差異が出る可能性がある
• C# Job Systemの概念を知ってほしい
• プログラマ以外は理解不能
諸注意
• 従来のマルチスレッドプログラミング
• C# Job System の概要
• Let’s read codes. (コードを読む)
• Let’s make a mistake. (間違ってみる)
• Let’s try “C# Job Compiler” (コンパイラを体験)
• Let’s implement. (実装してみる)
• まとめ
アジェンダ
従来のマルチスレッドプログラミングの話
マルチスレッド
プログラミングは
好きですか?
私は嫌いです
• レースコンディション対策が嫌だ
• 面倒臭い
• コードが汚い、可読性が低い
• 間違っても気づきにくい
MTPのここが嫌だ その1
Aが使うよ
Aが使うよ
Bが使うよ
A
AB
?
write
write read
• デッドロックが嫌だ
• 面倒臭い
• コードが汚い、可読性が低い
• 間違うと無限ループ
MTPのここが嫌だ その2
A
AB
B
待ち待ち
• 難解なところが嫌だ
• mutex:lockとか
• アトミック変数とか
MTPのここが嫌だ その3
• デバッグが嫌だ
• 正常に”動いてしまったり”する
• 無限ループになったりする
• 突然ハングアップしたりする
• リリース後にバグが判明したりする
MTPのここが嫌だ その4
私には無理だ!
そんなあなたに
C# Job System
C# Job System の概要
43伊藤周の年齢
116
116倍Boid シミュレーションを
マルチスレッドで 8 core CPUで
動かした場合の速度倍
Demo
• 簡潔に書ける
• GCフリー
• 安全
• 高速な新コンパイラ
C# Job System の特徴
• Data Oriented Programming
• データとビヘイビア(振舞い)の分離
• struct(構造体)コンポーネントの導入
• Job Component System の用意
• 簡潔に書けるようにマネージャーを用意
特徴1 簡潔に書ける
• GCをいかにさせないか
• NativeArrayの導入     
• 以下の感じで確保
• 以下の感じで解放(自分で)
特徴2 GCフリー
var src = new NativeArray<float>(500, Allocator.Temp);
src.Dispose();
 要素数 アロケーターの種類
  ↓     ↓
• 他のNativeArrayファミリー
特徴2 GCフリー
struct NativeArray<Value> // 配列
struct NativeList<Value> // リスト。追加削除が容易
struct NativeSlice<Value> // 一部を切り取れる
struct NativeHashmap<Key, Value> // Dictionary「
struct NativeMultiHashmap<Key, Value> //複数Dictionary
• エラーで指摘してくれる
• 落ちることはない
• レースコンディション、デッドロックは起こり得ない
• “Sandbox”
特徴3 安全
• C#→[Mono]→IL→[C# Job Compiler]→内部的な Domain
Model →[最適化]→[LLVM]→実行形式
• 10倍〜20倍高速になる
• 電池消費の軽減
• Why faster?
• SIMD命令の有効利用
• 正確さとパフォーマンスのトレードオフ
特徴4 高速な新コンパイラ
Let’s read C# Job System codes!
• IJob~でジョブを定義
• Execute にジョブの中身を書く
• Schedule でジョブを開始
• Complete でジョブ終了確認
• 変数はNativeArray系を使い、自力でDispose
コーディング基本まとめ
• IJob
• 1つのスレッドでジョブを回す
• IJobParallelFor
• 複数のスレッドでジョブを回す
• IJobParallelForTransform
• Transformにアクセスが可能
コーディング基本まとめ
public void Execute() {}
public void Execute(int i) {}
public void Execute(int i, TransformAccess transform){}
Let’s make a mistake!
• マルチスレッドプログラミングは間違えやすい
• ちょっとした見落としはしてしまう
• Unityは落ちることなくエラーが教えてくれる
• CTO Joachim「Unityは「Sandbox(=砂場)」である」
• 砂場では間違っていい。正解に導いてくれれれば。
エラーまとめ
Let’s try “C# Job コンパイラ”
• 一文付け足すだけ
• [ComputeJobOptimizationAttribute(Accuracy
.Med, Support.Relaxed)]
• Accuracy は計算の精度
• 新しいmathライブラリ
C# Job Compiler
• float1, float2, float3, float4,
• half1, half2, half3, half4
• int1, int2, int3, int4
• math.abs
• math.min
• math.max
• math.pow
• math.lerp
• math.clamp
• math.saturate
• math.select // 条件分岐
• math.rcp // 逆数
• math.sign
• math.rsqrt // sqrtの逆数
• math.any
• math.all
• math.sincos
新mathライブラリ
Let’s implement C# Job System.
public class RotatorOldUpdate : MonoBehaviour
{
[SerializeField]
float m_Speed;
public float speed
{
get { return m_Speed; }
set { m_Speed = value; }
}
void Update ()
{
transform.rotation = transform.rotation * Quaternion.AngleAxis (m_Speed *
Time.deltaTime, Vector3.up);
}
}
• STEP1:データレイアウトの最適化
• GameObjectごとにするのはやめる
• データをシーケンシャルにする
• キャッシュ化する
• forループでGetComponentとかしなくてよくなる
Job Component System実装まとめ
public class RotatorOldUpdate : MonoBehaviour
{
[SerializeField]
float m_Speed;
public float speed
{
get { return m_Speed; }
set { m_Speed = value; }
}
void Update ()
{
transform.rotation = transform.rotation * Quaternion.AngleAxis (m_Speed *
Time.deltaTime, Vector3.up);
}
}
class RotatorManagerMainThread : ScriptBehaviourManager
{
List<Transform> m_Transforms;
NativeList<float> m_Speeds;
:
protected override void OnUpdate()
{
base.OnUpdate ();
float deltaTime = Time.deltaTime;
NativeArray<float> speeds = m_Speeds;
for (int i = 0; i != m_Transforms.Count; i++)
{
var transform = m_Transforms [i];
transform.rotation = transform.rotation * Quaternion.AngleAxis (speeds[i] * deltaTime, Vector3.up);
}
}
:
:
}
public class RotatorWithManagerMainThread : ScriptBehaviour
{
:
(たくさんの実装)
:
}
• STEP2: Job化
• List<Transform> → TransformAccessArray
• IJobParallelForTransform継承したジョブ
• Execute(int index, TransformAccess
transform)の実装
Job Component System実装まとめ
class RotatorManagerMainThread : ScriptBehaviourManager
{
List<Transform> m_Transforms;
NativeList<float> m_Speeds;
:
protected override void OnUpdate()
{
base.OnUpdate ();
float deltaTime = Time.deltaTime;
NativeArray<float> speeds = m_Speeds;
for (int i = 0; i != m_Transforms.Count; i++)
{
var transform = m_Transforms [i];
transform.rotation = transform.rotation * Quaternion.AngleAxis (speeds[i] * deltaTime, Vector3.up);
}
}
:
:
}
public class RotatorWithManagerMainThread : ScriptBehaviour
{
:
(たくさんの実装)
:
}
class RotatorManager : ScriptBehaviourManager
{
TransformAccessArray m_Transforms;
NativeList<float> m_Speeds;
JobHandle m_Job;
:
protected override void OnUpdate()
{
base.OnUpdate ();
m_Job.Complete ();
var jobData = new RotatorJob();
jobData.speeds = m_Speeds;
jobData.deltaTime = Time.deltaTime;
m_Job = jobData.Schedule (m_Transforms);
}
struct RotatorJob : IJobParallelForTransform
{
[ReadOnly]
public NativeArray<float> speeds;
public float deltaTime;
public void Execute(int index, TransformAccess transform)
{
transform.rotation = transform.rotation * Quaternion.AngleAxis (speeds[index] * deltaTime, Vector3.up);
}
}
}
public class RotatorWithManager : ScriptBehaviour
{
:
(たくさんの実装)
:
}
• STEP3: データからビヘイビアを分離する
• ジョブで使用するデータを分離する
• InjectTuplesの導入
• Tuples が付加した配列はindexが同期する
• ComponentSystemから継承させる
• マネージャーの仕事を任せる
Job Component System実装まとめ
public class RotationSpeedComponent : ScriptBehaviour
{
public float speed;
}
public class RotatingSystem : ComponentSystem
{
[InjectTuples]
public ComponentArray<Transform> m_Transforms;
[InjectTuples]
public ComponentArray<RotationSpeedComponent> m_Rotators;
override protected void OnUpdate()
{
base.OnUpdate ();
float dt = Time.deltaTime;
for (int i = 0; i != m_Transforms.Length ;i++)
{
m_Transforms[i].rotation =
m_Transforms[i].rotation * Quaternion.AngleAxis(dt * m_Rotators[i].speed, Vector3.up);
}
}
}
• STEP4: データのstruct化
• MonoBehaviour継承 → IComponentData継承
• struct化
• ComponentSystemからの継承でお手軽マネー
ジャー
• ComponentArray → ComponentDataArray
Job Component System実装まとめ
public class RotationSpeedComponent : ScriptBehaviour
{
public float speed;
}
public class RotatingSystem : ComponentSystem
{
[InjectTuples]
public ComponentArray<Transform> m_Transforms;
[InjectTuples]
public ComponentArray<RotationSpeedComponent> m_Rotators;
override protected void OnUpdate()
{
base.OnUpdate ();
float dt = Time.deltaTime;
for (int i = 0; i != m_Transforms.Length ;i++)
{
m_Transforms[i].rotation =
m_Transforms[i].rotation * Quaternion.AngleAxis(dt * m_Rotators[i].speed, Vector3.up);
}
}
}
[Serializable]
public struct RotationSpeed : IComponentData
{
public float speed;
public RotationSpeed (float speed) { this.speed = speed; }
}
public class RotationSpeedDataComponent : ComponentDataWrapper<RotationSpeed> { }
public class RotatingDataSystem : ComponentSystem
{
[InjectTuples]
public ComponentArray<Transform> m_Transforms;
[InjectTuples]
public ComponentDataArray<RotationSpeed> m_Rotators;
override protected void OnUpdate()
{
base.OnUpdate ();
float dt = Time.deltaTime;
for (int i = 0; i != m_Transforms.Length ;i++)
{
m_Transforms[i].rotation =
m_Transforms[i].rotation * Quaternion.AngleAxis(dt * m_Rotators[i].speed, Vector3.up);
}
}
}
• STEP5: ジョブ実装 と 依存性解決
• IJobParallelForTransformを継承したstruct
• Execute で Transformが使える
• ComponentSystem → JobComponentSystem
• GetDependency()で依存性の自動解決
Job Component System実装まとめ
[Serializable]
public struct RotationSpeed : IComponentData
{
public float speed;
public RotationSpeed (float speed) { this.speed = speed; }
}
public class RotationSpeedDataComponent : ComponentDataWrapper<RotationSpeed> { }
public class RotatingDataSystem : ComponentSystem
{
[InjectTuples]
public ComponentArray<Transform> m_Transforms;
[InjectTuples]
public ComponentDataArray<RotationSpeed> m_Rotators;
override protected void OnUpdate()
{
base.OnUpdate ();
float dt = Time.deltaTime;
for (int i = 0; i != m_Transforms.Length ;i++)
{
m_Transforms[i].rotation =
m_Transforms[i].rotation * Quaternion.AngleAxis(dt * m_Rotators[i].speed, Vector3.up);
}
}
}
[Serializable]
public struct RotationSpeed : IComponentData
{
public float speed;
public RotationSpeed (float speed) { this.speed = speed; }
}
public class RotationSpeedDataComponent : ComponentDataWrapper<RotationSpeed> { }
public class SystemRotator : JobComponentSystem
{
[InjectTuples]
public TransformAccessArray m_Transforms;
[InjectTuples]
public ComponentDataArray<RotationSpeed> m_Rotators;
override protected void OnUpdate()
{
base.OnUpdate ();
var job = new Job();
job.dt = Time.deltaTime;
job.rotators = m_Rotators;
AddDependency(job.Schedule(m_Transforms, GetDependency ()));
}
struct Job : IJobParallelForTransform
{
public float dt;
[ReadOnly]
public ComponentDataArray<RotationSpeed> rotators;
public void Execute(int i, TransformAccess transform)
{
transform.rotation =
transform.rotation * Quaternion.AngleAxis(dt * rotators[i].speed, Vector3.up);
}
}
}
C# Job System 注意点 & まとめ
• データ構造はstructのみ (class はNG)
• .NETやUnity のAPIはジョブ内では(基本的に)使えない
• 何でもかんでも早くなるわけではない
• 算術系が早くなる、と考えるのが正解
• 相互の距離の計算とか
• 敵AIの思考ルーチンとか
C# Job System 注意点
• STEP1 C# Job system
• Unity 2017.3 or 2018.X
• STEP2 Component system
• STEP3 math library
• STEP4 C# Job Compiler
リリース予定
• 多数の敵・味方が出るゲームでの相互距離の計算
• それによるソート
• RTS等で使う影響マップの生成
• 弾幕シューティング
• etc…
実際に何に使える?
• マルチスレッドプログラミングが安全に書ける
• 新しいComponent System で簡潔に書ける
• コンパイラをかければさらに早くなる
C# Job System まとめ
Q&A

More Related Content

More from Unity Technologies Japan K.K.

今だから聞きたい!Unity2017/18ユーザーのためのUnity2019 LTS基礎知識
今だから聞きたい!Unity2017/18ユーザーのためのUnity2019 LTS基礎知識 今だから聞きたい!Unity2017/18ユーザーのためのUnity2019 LTS基礎知識
今だから聞きたい!Unity2017/18ユーザーのためのUnity2019 LTS基礎知識
Unity Technologies Japan K.K.
 

More from Unity Technologies Japan K.K. (20)

ビジュアルスクリプティング (旧:Bolt) で始めるUnity入門3日目 ゲームをカスタマイズしよう
ビジュアルスクリプティング (旧:Bolt) で始めるUnity入門3日目 ゲームをカスタマイズしようビジュアルスクリプティング (旧:Bolt) で始めるUnity入門3日目 ゲームをカスタマイズしよう
ビジュアルスクリプティング (旧:Bolt) で始めるUnity入門3日目 ゲームをカスタマイズしよう
 
ビジュアルスクリプティングで始めるUnity入門2日目 ゴールとスコアの仕組み - Unityステーション
ビジュアルスクリプティングで始めるUnity入門2日目 ゴールとスコアの仕組み - Unityステーションビジュアルスクリプティングで始めるUnity入門2日目 ゴールとスコアの仕組み - Unityステーション
ビジュアルスクリプティングで始めるUnity入門2日目 ゴールとスコアの仕組み - Unityステーション
 
ビジュアルスクリプティングで始めるUnity入門1日目 プレイヤーを動かそう
ビジュアルスクリプティングで始めるUnity入門1日目 プレイヤーを動かそうビジュアルスクリプティングで始めるUnity入門1日目 プレイヤーを動かそう
ビジュアルスクリプティングで始めるUnity入門1日目 プレイヤーを動かそう
 
PlasticSCMの活用テクニックをハンズオンで一緒に学ぼう!
PlasticSCMの活用テクニックをハンズオンで一緒に学ぼう!PlasticSCMの活用テクニックをハンズオンで一緒に学ぼう!
PlasticSCMの活用テクニックをハンズオンで一緒に学ぼう!
 
点群を使いこなせ! 可視化なんて当たり前、xRと点群を組み合わせたUnityの世界 【Interact , Stipple】
点群を使いこなせ! 可視化なんて当たり前、xRと点群を組み合わせたUnityの世界 【Interact , Stipple】点群を使いこなせ! 可視化なんて当たり前、xRと点群を組み合わせたUnityの世界 【Interact , Stipple】
点群を使いこなせ! 可視化なんて当たり前、xRと点群を組み合わせたUnityの世界 【Interact , Stipple】
 
Unity教える先生方注目!ティーチャートレーニングデイを体験しよう
Unity教える先生方注目!ティーチャートレーニングデイを体験しようUnity教える先生方注目!ティーチャートレーニングデイを体験しよう
Unity教える先生方注目!ティーチャートレーニングデイを体験しよう
 
「原神」におけるコンソールプラットフォーム開発
「原神」におけるコンソールプラットフォーム開発「原神」におけるコンソールプラットフォーム開発
「原神」におけるコンソールプラットフォーム開発
 
FANTASIANの明日使えない特殊テクニック教えます
FANTASIANの明日使えない特殊テクニック教えますFANTASIANの明日使えない特殊テクニック教えます
FANTASIANの明日使えない特殊テクニック教えます
 
インディーゲーム開発の現状と未来 2021
インディーゲーム開発の現状と未来 2021インディーゲーム開発の現状と未来 2021
インディーゲーム開発の現状と未来 2021
 
建築革命、更に進化!デジタルツイン基盤の真打ち登場【概要編 Unity Reflect ver 2.1 】
建築革命、更に進化!デジタルツイン基盤の真打ち登場【概要編 Unity Reflect ver 2.1 】建築革命、更に進化!デジタルツイン基盤の真打ち登場【概要編 Unity Reflect ver 2.1 】
建築革命、更に進化!デジタルツイン基盤の真打ち登場【概要編 Unity Reflect ver 2.1 】
 
Burstを使ってSHA-256のハッシュ計算を高速に行う話
Burstを使ってSHA-256のハッシュ計算を高速に行う話Burstを使ってSHA-256のハッシュ計算を高速に行う話
Burstを使ってSHA-256のハッシュ計算を高速に行う話
 
Cinemachineで見下ろし視点のカメラを作る
Cinemachineで見下ろし視点のカメラを作るCinemachineで見下ろし視点のカメラを作る
Cinemachineで見下ろし視点のカメラを作る
 
徹底解説 Unity Reflect【開発編 ver2.0】
徹底解説 Unity Reflect【開発編 ver2.0】徹底解説 Unity Reflect【開発編 ver2.0】
徹底解説 Unity Reflect【開発編 ver2.0】
 
徹底解説 Unity Reflect【概要編 ver2.0】
徹底解説 Unity Reflect【概要編 ver2.0】徹底解説 Unity Reflect【概要編 ver2.0】
徹底解説 Unity Reflect【概要編 ver2.0】
 
Unityティーチャートレーニングデイ -認定プログラマー編-
Unityティーチャートレーニングデイ -認定プログラマー編-Unityティーチャートレーニングデイ -認定プログラマー編-
Unityティーチャートレーニングデイ -認定プログラマー編-
 
Unityティーチャートレーニングデイ -認定3Dアーティスト編-
Unityティーチャートレーニングデイ -認定3Dアーティスト編-Unityティーチャートレーニングデイ -認定3Dアーティスト編-
Unityティーチャートレーニングデイ -認定3Dアーティスト編-
 
Unityティーチャートレーニングデイ -認定アソシエイト編-
Unityティーチャートレーニングデイ -認定アソシエイト編-Unityティーチャートレーニングデイ -認定アソシエイト編-
Unityティーチャートレーニングデイ -認定アソシエイト編-
 
今だから聞きたい!Unity2017/18ユーザーのためのUnity2019 LTS基礎知識
今だから聞きたい!Unity2017/18ユーザーのためのUnity2019 LTS基礎知識 今だから聞きたい!Unity2017/18ユーザーのためのUnity2019 LTS基礎知識
今だから聞きたい!Unity2017/18ユーザーのためのUnity2019 LTS基礎知識
 
【Unity Reflect】無料のViewerに機能が増えた!?~お披露目会編~
【Unity Reflect】無料のViewerに機能が増えた!?~お披露目会編~【Unity Reflect】無料のViewerに機能が増えた!?~お披露目会編~
【Unity Reflect】無料のViewerに機能が増えた!?~お披露目会編~
 
ビジュアルスクリプティングシステムBoltを使ってみよう 2回目
ビジュアルスクリプティングシステムBoltを使ってみよう 2回目ビジュアルスクリプティングシステムBoltを使ってみよう 2回目
ビジュアルスクリプティングシステムBoltを使ってみよう 2回目
 

【CEDEC2017】C#JobSystem を使った Unity流マルチスレッドプログラミング

  • 2. • 今回紹介するC# Job Systemはまだ発展段階 • リリースでは多少の差異が出る可能性がある • C# Job Systemの概念を知ってほしい • プログラマ以外は理解不能 諸注意
  • 3. • 従来のマルチスレッドプログラミング • C# Job System の概要 • Let’s read codes. (コードを読む) • Let’s make a mistake. (間違ってみる) • Let’s try “C# Job Compiler” (コンパイラを体験) • Let’s implement. (実装してみる) • まとめ アジェンダ
  • 7. • レースコンディション対策が嫌だ • 面倒臭い • コードが汚い、可読性が低い • 間違っても気づきにくい MTPのここが嫌だ その1 Aが使うよ Aが使うよ Bが使うよ A AB ? write write read
  • 8. • デッドロックが嫌だ • 面倒臭い • コードが汚い、可読性が低い • 間違うと無限ループ MTPのここが嫌だ その2 A AB B 待ち待ち
  • 9. • 難解なところが嫌だ • mutex:lockとか • アトミック変数とか MTPのここが嫌だ その3
  • 10. • デバッグが嫌だ • 正常に”動いてしまったり”する • 無限ループになったりする • 突然ハングアップしたりする • リリース後にバグが判明したりする MTPのここが嫌だ その4
  • 13. C# Job System の概要
  • 15. 116
  • 16.
  • 17.
  • 18. 116倍Boid シミュレーションを マルチスレッドで 8 core CPUで 動かした場合の速度倍
  • 19. Demo
  • 20. • 簡潔に書ける • GCフリー • 安全 • 高速な新コンパイラ C# Job System の特徴
  • 21. • Data Oriented Programming • データとビヘイビア(振舞い)の分離 • struct(構造体)コンポーネントの導入 • Job Component System の用意 • 簡潔に書けるようにマネージャーを用意 特徴1 簡潔に書ける
  • 22. • GCをいかにさせないか • NativeArrayの導入      • 以下の感じで確保 • 以下の感じで解放(自分で) 特徴2 GCフリー var src = new NativeArray<float>(500, Allocator.Temp); src.Dispose();  要素数 アロケーターの種類   ↓     ↓
  • 23. • 他のNativeArrayファミリー 特徴2 GCフリー struct NativeArray<Value> // 配列 struct NativeList<Value> // リスト。追加削除が容易 struct NativeSlice<Value> // 一部を切り取れる struct NativeHashmap<Key, Value> // Dictionary「 struct NativeMultiHashmap<Key, Value> //複数Dictionary
  • 24. • エラーで指摘してくれる • 落ちることはない • レースコンディション、デッドロックは起こり得ない • “Sandbox” 特徴3 安全
  • 25. • C#→[Mono]→IL→[C# Job Compiler]→内部的な Domain Model →[最適化]→[LLVM]→実行形式 • 10倍〜20倍高速になる • 電池消費の軽減 • Why faster? • SIMD命令の有効利用 • 正確さとパフォーマンスのトレードオフ 特徴4 高速な新コンパイラ
  • 26. Let’s read C# Job System codes!
  • 27. • IJob~でジョブを定義 • Execute にジョブの中身を書く • Schedule でジョブを開始 • Complete でジョブ終了確認 • 変数はNativeArray系を使い、自力でDispose コーディング基本まとめ
  • 28. • IJob • 1つのスレッドでジョブを回す • IJobParallelFor • 複数のスレッドでジョブを回す • IJobParallelForTransform • Transformにアクセスが可能 コーディング基本まとめ public void Execute() {} public void Execute(int i) {} public void Execute(int i, TransformAccess transform){}
  • 29. Let’s make a mistake!
  • 30. • マルチスレッドプログラミングは間違えやすい • ちょっとした見落としはしてしまう • Unityは落ちることなくエラーが教えてくれる • CTO Joachim「Unityは「Sandbox(=砂場)」である」 • 砂場では間違っていい。正解に導いてくれれれば。 エラーまとめ
  • 31. Let’s try “C# Job コンパイラ”
  • 32. • 一文付け足すだけ • [ComputeJobOptimizationAttribute(Accuracy .Med, Support.Relaxed)] • Accuracy は計算の精度 • 新しいmathライブラリ C# Job Compiler
  • 33. • float1, float2, float3, float4, • half1, half2, half3, half4 • int1, int2, int3, int4 • math.abs • math.min • math.max • math.pow • math.lerp • math.clamp • math.saturate • math.select // 条件分岐 • math.rcp // 逆数 • math.sign • math.rsqrt // sqrtの逆数 • math.any • math.all • math.sincos 新mathライブラリ
  • 34. Let’s implement C# Job System.
  • 35. public class RotatorOldUpdate : MonoBehaviour { [SerializeField] float m_Speed; public float speed { get { return m_Speed; } set { m_Speed = value; } } void Update () { transform.rotation = transform.rotation * Quaternion.AngleAxis (m_Speed * Time.deltaTime, Vector3.up); } }
  • 36. • STEP1:データレイアウトの最適化 • GameObjectごとにするのはやめる • データをシーケンシャルにする • キャッシュ化する • forループでGetComponentとかしなくてよくなる Job Component System実装まとめ
  • 37. public class RotatorOldUpdate : MonoBehaviour { [SerializeField] float m_Speed; public float speed { get { return m_Speed; } set { m_Speed = value; } } void Update () { transform.rotation = transform.rotation * Quaternion.AngleAxis (m_Speed * Time.deltaTime, Vector3.up); } }
  • 38. class RotatorManagerMainThread : ScriptBehaviourManager { List<Transform> m_Transforms; NativeList<float> m_Speeds; : protected override void OnUpdate() { base.OnUpdate (); float deltaTime = Time.deltaTime; NativeArray<float> speeds = m_Speeds; for (int i = 0; i != m_Transforms.Count; i++) { var transform = m_Transforms [i]; transform.rotation = transform.rotation * Quaternion.AngleAxis (speeds[i] * deltaTime, Vector3.up); } } : : } public class RotatorWithManagerMainThread : ScriptBehaviour { : (たくさんの実装) : }
  • 39. • STEP2: Job化 • List<Transform> → TransformAccessArray • IJobParallelForTransform継承したジョブ • Execute(int index, TransformAccess transform)の実装 Job Component System実装まとめ
  • 40. class RotatorManagerMainThread : ScriptBehaviourManager { List<Transform> m_Transforms; NativeList<float> m_Speeds; : protected override void OnUpdate() { base.OnUpdate (); float deltaTime = Time.deltaTime; NativeArray<float> speeds = m_Speeds; for (int i = 0; i != m_Transforms.Count; i++) { var transform = m_Transforms [i]; transform.rotation = transform.rotation * Quaternion.AngleAxis (speeds[i] * deltaTime, Vector3.up); } } : : } public class RotatorWithManagerMainThread : ScriptBehaviour { : (たくさんの実装) : }
  • 41. class RotatorManager : ScriptBehaviourManager { TransformAccessArray m_Transforms; NativeList<float> m_Speeds; JobHandle m_Job; : protected override void OnUpdate() { base.OnUpdate (); m_Job.Complete (); var jobData = new RotatorJob(); jobData.speeds = m_Speeds; jobData.deltaTime = Time.deltaTime; m_Job = jobData.Schedule (m_Transforms); } struct RotatorJob : IJobParallelForTransform { [ReadOnly] public NativeArray<float> speeds; public float deltaTime; public void Execute(int index, TransformAccess transform) { transform.rotation = transform.rotation * Quaternion.AngleAxis (speeds[index] * deltaTime, Vector3.up); } } } public class RotatorWithManager : ScriptBehaviour { : (たくさんの実装) : }
  • 42. • STEP3: データからビヘイビアを分離する • ジョブで使用するデータを分離する • InjectTuplesの導入 • Tuples が付加した配列はindexが同期する • ComponentSystemから継承させる • マネージャーの仕事を任せる Job Component System実装まとめ
  • 43. public class RotationSpeedComponent : ScriptBehaviour { public float speed; } public class RotatingSystem : ComponentSystem { [InjectTuples] public ComponentArray<Transform> m_Transforms; [InjectTuples] public ComponentArray<RotationSpeedComponent> m_Rotators; override protected void OnUpdate() { base.OnUpdate (); float dt = Time.deltaTime; for (int i = 0; i != m_Transforms.Length ;i++) { m_Transforms[i].rotation = m_Transforms[i].rotation * Quaternion.AngleAxis(dt * m_Rotators[i].speed, Vector3.up); } } }
  • 44. • STEP4: データのstruct化 • MonoBehaviour継承 → IComponentData継承 • struct化 • ComponentSystemからの継承でお手軽マネー ジャー • ComponentArray → ComponentDataArray Job Component System実装まとめ
  • 45. public class RotationSpeedComponent : ScriptBehaviour { public float speed; } public class RotatingSystem : ComponentSystem { [InjectTuples] public ComponentArray<Transform> m_Transforms; [InjectTuples] public ComponentArray<RotationSpeedComponent> m_Rotators; override protected void OnUpdate() { base.OnUpdate (); float dt = Time.deltaTime; for (int i = 0; i != m_Transforms.Length ;i++) { m_Transforms[i].rotation = m_Transforms[i].rotation * Quaternion.AngleAxis(dt * m_Rotators[i].speed, Vector3.up); } } }
  • 46. [Serializable] public struct RotationSpeed : IComponentData { public float speed; public RotationSpeed (float speed) { this.speed = speed; } } public class RotationSpeedDataComponent : ComponentDataWrapper<RotationSpeed> { } public class RotatingDataSystem : ComponentSystem { [InjectTuples] public ComponentArray<Transform> m_Transforms; [InjectTuples] public ComponentDataArray<RotationSpeed> m_Rotators; override protected void OnUpdate() { base.OnUpdate (); float dt = Time.deltaTime; for (int i = 0; i != m_Transforms.Length ;i++) { m_Transforms[i].rotation = m_Transforms[i].rotation * Quaternion.AngleAxis(dt * m_Rotators[i].speed, Vector3.up); } } }
  • 47. • STEP5: ジョブ実装 と 依存性解決 • IJobParallelForTransformを継承したstruct • Execute で Transformが使える • ComponentSystem → JobComponentSystem • GetDependency()で依存性の自動解決 Job Component System実装まとめ
  • 48. [Serializable] public struct RotationSpeed : IComponentData { public float speed; public RotationSpeed (float speed) { this.speed = speed; } } public class RotationSpeedDataComponent : ComponentDataWrapper<RotationSpeed> { } public class RotatingDataSystem : ComponentSystem { [InjectTuples] public ComponentArray<Transform> m_Transforms; [InjectTuples] public ComponentDataArray<RotationSpeed> m_Rotators; override protected void OnUpdate() { base.OnUpdate (); float dt = Time.deltaTime; for (int i = 0; i != m_Transforms.Length ;i++) { m_Transforms[i].rotation = m_Transforms[i].rotation * Quaternion.AngleAxis(dt * m_Rotators[i].speed, Vector3.up); } } }
  • 49. [Serializable] public struct RotationSpeed : IComponentData { public float speed; public RotationSpeed (float speed) { this.speed = speed; } } public class RotationSpeedDataComponent : ComponentDataWrapper<RotationSpeed> { } public class SystemRotator : JobComponentSystem { [InjectTuples] public TransformAccessArray m_Transforms; [InjectTuples] public ComponentDataArray<RotationSpeed> m_Rotators; override protected void OnUpdate() { base.OnUpdate (); var job = new Job(); job.dt = Time.deltaTime; job.rotators = m_Rotators; AddDependency(job.Schedule(m_Transforms, GetDependency ())); } struct Job : IJobParallelForTransform { public float dt; [ReadOnly] public ComponentDataArray<RotationSpeed> rotators; public void Execute(int i, TransformAccess transform) { transform.rotation = transform.rotation * Quaternion.AngleAxis(dt * rotators[i].speed, Vector3.up); } } }
  • 50. C# Job System 注意点 & まとめ
  • 51. • データ構造はstructのみ (class はNG) • .NETやUnity のAPIはジョブ内では(基本的に)使えない • 何でもかんでも早くなるわけではない • 算術系が早くなる、と考えるのが正解 • 相互の距離の計算とか • 敵AIの思考ルーチンとか C# Job System 注意点
  • 52. • STEP1 C# Job system • Unity 2017.3 or 2018.X • STEP2 Component system • STEP3 math library • STEP4 C# Job Compiler リリース予定
  • 53. • 多数の敵・味方が出るゲームでの相互距離の計算 • それによるソート • RTS等で使う影響マップの生成 • 弾幕シューティング • etc… 実際に何に使える?
  • 54. • マルチスレッドプログラミングが安全に書ける • 新しいComponent System で簡潔に書ける • コンパイラをかければさらに早くなる C# Job System まとめ
  • 55. Q&A