More Related Content
More from Unity Technologies Japan K.K. (20)
【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. (実装してみる)
• まとめ
アジェンダ
- 21. • Data Oriented Programming
• データとビヘイビア(振舞い)の分離
• struct(構造体)コンポーネントの導入
• Job Component System の用意
• 簡潔に書けるようにマネージャーを用意
特徴1 簡潔に書ける
- 25. • C#→[Mono]→IL→[C# Job Compiler]→内部的な Domain
Model →[最適化]→[LLVM]→実行形式
• 10倍〜20倍高速になる
• 電池消費の軽減
• Why faster?
• SIMD命令の有効利用
• 正確さとパフォーマンスのトレードオフ
特徴4 高速な新コンパイラ
- 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){}
- 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ライブラリ
- 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);
}
}
- 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);
}
}
}
- 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
リリース予定