Weitere ähnliche Inhalte
Ähnlich wie 【Unite Tokyo 2018 Training Day】C#JobSystem & ECSでCPUを極限まで使い倒そう ~C# JobSystem 編~ (20)
Mehr von Unity Technologies Japan K.K. (20)
【Unite Tokyo 2018 Training Day】C#JobSystem & ECSでCPUを極限まで使い倒そう ~C# JobSystem 編~
- 2. • Unity
4
• Native Container
• 5.6 2018.1β
• C# JobSystem
• 2018.1 β
• Entity Conponent System(ECS)
• 2018.1.0 Experimental( )
• Burst Compiler
• 2018.1.0 Experimental( )
- 3. • Unity
4
• Native Container
• 5.6 2018.1β
• C# JobSystem
• 2018.1 β
• Entity Conponent System(ECS)
• 2018.1β
• Burst Compiler
• 2018.1β
私からの話はコチラになります。
- 11. NativeContainer
int や float 等のプリミティブなタイプか、
Vector3等のstruct型のみが指定可能です。
class型は指定不可です
// Vector3 NativeArray
NativeArray<Vector3>array = new NativeArray<Vector3>(size, Allocator.Persistent);
//
for( int i = 0; i < array.Length; ++i ){
array[ i ] = array[i] + Vector3.one;
}
//
array.Dispose();
- 12. // Vector3 NativeArray
NativeArray<Vector3>array = new NativeArray<Vector3>(size, Allocator.Persistent);
//
for( int i = 0; i < array.Length; ++i ){
array[ i ] = array[i] + Vector3.one;
}
//
array.Dispose();
NativeContainer
メモリの生存期間に合わせて、以下の三つの中から選択可能です。
Allocator.Temp ( そのフレームのみ有効)
Allocator.TempForJob(そのJob中のみ有効)
Allocator.Persistent(解放するまで有効)
下に行くほど、メモリ確保のコストが高くなります。
- 24. Job
• 3 Job Unity
• IJob
• IJobParalellFor
• Transform
IJobParallelForTransform
- 25. IJob(1)
// Jobの定義部分
struct MyJob : IJob{
// Jobに渡したいパラメーター
public int param;
// 実際に実行する処理
public void Execute( ){
int sum = 0;
for( int i = 0 ; i < param ; ++ i){ sum += i ; }
Debug.Log( “sum “ + sum );
}
}
// MonoBehaviourのUpdate内等で実際にJobを発行するところ
// Jobを作成します
var job = new MyJob(){
param = 100
};
// Jobを発行してます
JobHandlehandle= job.Schedule();
// 発行したJobが完了するのを待ちます
handle.Complete();
- 28. IJobParalellFor
NativeArray<Vector3 >position
[0]
( 0, 0, 10 )
[1]
( 0, 0, 20 )
[2]
( 0, 0, 30 )
[3]
( 0, 0, 40 )
[4]
( 0, 0, 50 )
( 0, 0, 11) ( 0, 0, 21 ) ( 0, 0, 31 ) ( 0, 0, 41 ) ( 0, 0, 51 )
これに対して、(0,0,1 )を足していく
IJobParallelForを使えば、この「(0,0,1) を足す作業」を
平行して処理させることが出来ます。
...
...
- 29. MainThread
position [ 0~10 ]の更新処理
Worker position [ 11~20 ]の更新処理
Worker position [ 21~30 ]の更新処理
Jobの発行及び完了待ち
分散後
MainThread position [ 0~30 ]の更新処理
分散前
- 30. // Jobの定義部分
struct MyPositionUpdate : IJobParallelFor{
// 更新対象をNativeArrayの形で保持します
public NativeArray<Vector3> positions;
public float deltaTime;
// 実行部分 indexは、更新対象をする配列のindex値が入ります。
public void Execute( int index ){
positions[ index ] = positions[ index ] + Vector3.front * deltaTime;
}
}
// MonoBehaviourのUpdate内等で実際にJobを発行するところ
// Jobを作成します
var job = new MyPositionUpdate(){
positions = bulletPositions,// <-NativeArray<Vector3> bulletPositions;が定義されているつもり
deltaTime= Time.deltaTime,//<- Main以外からはTime.deltaTimeが呼べないので…
};
// Jobを発行してます。対象の配列要素数・バッチ数を指定(0で良い感じに設定してくれる)
JobHandlehandle= job.Schedule( positions.Length,0 );
// 発行したJobが完了するのを待ちます
handle.Complete();
IJobParalellFor
- 31. // Jobの定義部分
struct MyPositionUpdate : IJobParallelFor{
// 更新対象をNativeArrayの形で保持します
public NativeArray<Vector3> positions;
public float deltaTime;
// 実行部分 indexは、更新対象をする配列のindex値が入ります。
public void Execute( int index ){
positions[ index ] = positions[ index ] + Vector3.up * deltaTime;
}
}
更新の対象は、Nativeコンテナである必要があります。
IJobParalellFor
// MonoBehaviourのUpdate内等で実際にJobを発行するところ
// Jobを作成します
var job = new MyPositionUpdate(){
positions = bulletPositions,// <-NativeArray<Vector3> bulletPositions;が定義されているつもり
deltaTime= Time.deltaTime,//<- Main以外からはTime.deltaTimeが呼べないので…
};
// Jobを発行してます。対象の配列要素数・バッチ数を指定(0で良い感じに設定してくれる)
JobHandlehandle= job.Schedule( positions.Length,0 );
// 発行したJobが完了するのを待ちます
handle.Complete();
- 32. // Jobの定義部分
struct MyPositionUpdate : IJobParallelFor{
// 更新対象をNativeArrayの形で保持します
public NativeArray<Vector3> positions;
public float deltaTime;
// 実行部分 indexは、更新対象をする配列のindex値が入ります。
public void Execute( int index ){
positions[ index ] = positions[ index ] + Vector3.up * deltaTime;
}
}
IJobParalellFor
// MonoBehaviourのUpdate内等で実際にJobを発行するところ
// Jobを作成します
var job = new MyPositionUpdate(){
positions = bulletPositions,// <-NativeArray<Vector3> bulletPositions;が定義されているつもり
deltaTime= Time.deltaTime,//<- Main以外からはTime.deltaTimeが呼べないので…
};
// Jobを発行してます。対象の配列要素数・バッチ数を指定(0で良い感じに設定してくれる)
JobHandlehandle= job.Schedule( positions.Length,0 );
// 発行したJobが完了するのを待ちます
handle.Complete();
予期せぬエラーを防ぐため、引数で渡された「index」以外に
書き込もうとすると、Editor上ではエラーメッセージが出るよ
うになっています。
- 34. // Jobの定義部分
struct MyTransformUpdateJob : IJobParallelForTransform{
// Jobに渡したいパラメーター
public int objNum;
public float time;
// 実際に実行する処理 indexには配列の何個目であるか、transform に対して値をセットします
public void Execute(int index, TransformAccesstransform){
transform.position = new Vector3( index – objNum / 2 , time , 0.0f);
}
}
// MonoBehaviourのUpdate内等で実際にJobを発行するところ
Transform[] transformArray = "xxx";
// 並行して処理をしたいTransformのリストを定義します
var transformAccessArray = new TransformAccessArray(transformArray);
// Jobを作成します
var myTransformUpdateJob = new MyTransformUpdateJob(){
time= Time.deltaTime , objNum = transformArray.Length
};
// Jobを発行して、直後で完了待ち
JobHandlehandle= myTransformUpdateJob.Schedule( transformAccessArray );
handle.Complete();
複数のTransformに対して並行して処理を行うための
IJobParallelForTransform
- 35. // MonoBehaviourのUpdate内等で実際にJobを発行するところ
Transform[] transformArray = "xxx";
// 並行して処理をしたいTransformのリストを定義します
var transformAccessArray = new TransformAccessArray(transformArray);
// Jobを作成します
var myTransformUpdateJob = new MyTransformUpdateJob(){
time= Time.deltaTime , objNum = transformArray.Length
};
// Jobを発行して、直後で完了待ち
JobHandlehandle= myTransformUpdateJob.Schedule( transformAccessArray );
handle.Complete();
複数のTransformに対して並行して処理を行うための
IJobParallelForTransform
引数で渡ってくるTransformAccess経由でpositionやrotationを
設定します。
// Jobの定義部分
struct MyTransformUpdateJob : IJobParallelForTransform{
// Jobに渡したいパラメーター
public int objNum;
public float time;
// 実際に実行する処理 indexには配列の何個目であるか、transform に対して値をセットします
public void Execute(int index, TransformAccesstransform){
transform.position = new Vector3( index – objNum / 2 , time , 0.0f);
}
}
- 45. Job
Worker
Job A
NativeArray<Vector3 >positions
[0]
( 0, 0, 10 )
[1]
( 0, 0, 20 )
[2]
( 0, 0, 30 )
[3]
( 0, 0, 40 )
[4]
( 0, 0, 50 )
...
...
positions
Worker
Job B
positions
- 46. Job
Worker
Job A
NativeArray<Vector3 >positions
[0]
( 0, 0, 10 )
[1]
( 0, 0, 20 )
[2]
( 0, 0, 30 )
[3]
( 0, 0, 40 )
[4]
( 0, 0, 50 )
...
...
positions
Worker
Job B
positions
同時に走っているJobが同じ参照先( NativeContainer )を指
していた場合に複数のJobから同一のリソース書き込みを
される可能性を考慮してEditor実行時にはエラーを出すよ
うにしています。
- 47. Job
Worker
Job A
NativeArray<Vector3 >positions
[0]
( 0, 0, 10 )
[1]
( 0, 0, 20 )
[2]
( 0, 0, 30 )
[3]
( 0, 0, 40 )
[4]
( 0, 0, 50 )
...
...
positions
Worker
Job B
positions
書き込みを行わないのであれば、Jobの変数宣言に[ReadOnly] 属性をつけて、
このpositionsへの書き込みは行わない事を明示的に教えます。
struct MyJob : IJob{
[ReadOnly]
public NativeArray<Vector3>positions;
}
また[ReadOnly]の反対で、[WriteOnly]属性もあります。
- 49. IJobParallelFor
// Jobの定義部分
struct MyPositionUpdate : IJobParallelFor{
// 更新対象をNativeArrayの形で保持します
public NativeArray<Vector3> positions;
// 敵のタイプ別の速度
public NativeArray<Vector3> velocityByEnemyType;
// 敵のタイプ指定
public NativeArray<int> enemyType;
// deltaTIme
public float deltaTime;
// 実行部分 indexは、更新対象をする配列のindex値が入ります。
public void Execute( int index ){
int enemyType = enemyType[index];
Vector3 velocity = velocityByEnemyType[ enemyType ];
positions[ index ] = positions[ index ] + Vector3.front * deltaTime;
}
}
- 50. IJobParallelFor
// Jobの定義部分
struct MyPositionUpdate : IJobParallelFor{
// 更新対象をNativeArrayの形で保持します
public NativeArray<Vector3> positions;
// 敵のタイプ別の速度
public NativeArray<Vector3> velocityByEnemyType;
// 敵のタイプ指定
public NativeArray<int> enemyType;
// deltaTIme
public float deltaTime;
// 実行部分 indexは、更新対象をする配列のindex値が入ります。
public void Execute( int index ){
int enemyType = enemyType[index];
Vector3 velocity = velocityByEnemyType[ enemyType ];
positions[ index ] = positions[ index ] + Vector3.front * deltaTime;
}
}
ここで実行時にエラーが出ます。
これは velocityByEnemyType[ enemyType ]と、引数「index」以外にアクセスし
ているためです。
アクセスがReadなのかWriteなのかまでは判別は判別できませんが、指定され
たindex以外にアクセスが起きている事だけは UnityEditor側で検知が出来ます。
なので、実行時にエラーを出します
- 51. IJobParallelFor
// Jobの定義部分
struct MyPositionUpdate : IJobParallelFor{
// 更新対象をNativeArrayの形で保持します
public NativeArray<Vector3> positions;
// 敵のタイプ別の速度
[ReadOnly]
public NativeArray<Vector3> velocityByEnemyType;
// 敵のタイプ指定
public NativeArray<int> enemyType;
// deltaTIme
public float deltaTime;
// 実行部分 indexは、更新対象をする配列のindex値が入ります。
public void Execute( int index ){
int enemyType = enemyType[index];
Vector3 velocity = velocityByEnemyType[ enemyType ];
positions[ index ] = positions[ index ] + Vector3.front * deltaTime;
}
}
velocityByEnemyTypeに対して [ReadOnly]属性をつけてあげる事で、
velocityByEnemyTypeへの書き込みを行わないことを明示する事で実行時エラーが
回避できます。
- 53. Update
class JobTest : MonoBehaviour{
privateJobHandlehandle;
public Transform[] transformArray;
void Update(){
//前のフレームで発行したJobが完了するのを待ちます
jobHandle.Complete();
// 並行して処理をしたいTransformのリストを定義します
var transformAccessArray = new TransformAccessArray(transformArray);
// Jobを作成します
var myTransformUpdateJob = new MyTransformUpdateJob(){
time = Time.deltaTime, objNum = transformArray.Length
};
// Jobを発行してます。この時点では、Todo用のQueueに積まれるだけです
JobHandlehandle= myTransformUpdateJob.Schedule( transformAccessArray );
// [重要] 実際に積まれたJobの実行を促します
JobHandle.ScheduleBatchedJobs();
}
}