【Unite Tokyo 2018 Training Day】C#JobSystem & ECSでCPUを極限まで使い倒そう ~C# JobSystem 編~

2.6K Views

April 23, 18

スライド概要

Unite Tokyo 2018 Training Day「C#JobSystem & ECSでCPUを極限まで使い倒そう ~C# JobSystem 編~」の資料です。

講師:黒河 優介(ディベロッパーリレーションエンジニア|ユニティ・テクノロジーズ・ジャパン合同会社)

※【Unite Tokyo 2018 Training Day】C#JobSystem & ECSでCPUを極限まで使い倒そう ~Entity Component System 編~ の資料はこちら
https://www.slideshare.net/UnityTechnologiesJapan/unite-tokyo-2018-training-daycjobsystem-ecscpu-entity-component-system-1

■ワークショップで利用したUnityプロジェクト
https://wotakuro.github.io/JobSystemWorkshop/JobSystemWorkshop.zip

■ワークショップ内容
C# Job System、ECS(Entity Component System)がUnity2018で使えるようになりました。

C# Job SystemはUnityのシステムと親和性が高く、安全なマルチスレッドプログラミングを容易にする新機能です。ECSは非常に高速な、新しいオブジェクトの管理システムです。この二つを組み合わせて、CPUのパワーを非常に効率よく利用することができます。

※このセッションは、受講者がUnity上での基本的なC#プログラミング・マルチスレッドプログラミングに関する基本的な知識を持っている前提で行います。

※ECSは現在ベータ段階で今後も調整、変更が重ねられる予定です。

profile-image

リアルタイム3Dコンテンツを制作・運用するための世界的にリードするプラットフォームである「Unity」の日本国内における販売、サポート、コミュニティ活動、研究開発、教育支援を行っています。ゲーム開発者からアーティスト、建築家、自動車デザイナー、映画製作者など、さまざまなクリエイターがUnityを使い想像力を発揮しています。

シェア

またはPlayer版

埋め込む »CMSなどでJSが使えない場合

関連スライド

各ページのテキスト
1.

2018/4/21 C#JobSystem & ECSでCPUを極限まで 使い倒そう ~C# JobSystem編~ 講演者名 黒河 優介 所属団体 Unity Technologies Japan 肩書・役職 Developer Relation Engineer

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β‫׌תכח‬劢㹋鄲կ植㖈 ‫ؙٗ‬٦‫דسؤ‬ꂁ䋒⚥կ

4.

ֿ‫ךדת׸‬Unity‫㉏ך‬겗挿 • GCָꅾְ • C#欽‫ח‬然⥂ׁ‫כًٌٔ׋׸‬ծ،‫ٔف‬穄✪‫דת‬鵤ׁ‫ְז׸‬ • 僇爙涸‫ח‬Thread‫׾‬甧‫הְזג‬ծC#‫ָגץׅרקךزفؙٔأ‬MainThread ♳‫ד‬㹋遤ׁ‫׷׸‬

5.

NativeContainer‫ד‬箢ㄤׁ‫㉏׷׸‬겗挿 • GCָꅾְ • 孡鯪‫ ח‬newָ⳿勻‫ְז‬ • C#欽‫ח‬然⥂ׁ‫כًٌٔ׋׸‬ծ،‫ٔف‬穄✪‫דת‬鵤ׁ‫ְז׸‬ • ♧儗涸‫ؿحغז‬؋٦‫׾‬然⥂‫ה׷ׅ‬ծ⟃䖓‫׾ًٌٔך‬㖇鶕‫׷ׅ‬

6.

‫‪㉏겗挿‬ך‪GC‬‬ ‫ًٌٔ‪$‬ח׋‪ծ倜‬ד♴‪ְ朐屣‬ז׶‪$ָًٌٔ駈‬‬ ‫ًٌٔ‪$‬גׇ׵‪饥‬׾‪儗ծ($‬׋׃הֲ״׃⥂‪然‬׾‬ ‫‪կ‬ׅת׃הֲ״ֽ‪瑞‬׾‬ ‫في‪٦‬؜‪ծ‬׭׋ְ׋‪ꅾ‬ח‪($Ⳣ椚ָꬊ䌢‬ךֿ‪ָծ‬‬ ‫ׅת׃‪ָ涪欰‬ؗخؙؕח⚥؎ٖ‬

7.

ManagedMemory‫㉏ך‬겗挿 $欽‫♧ג׃ה‬䏝然⥂ׁ‫חيذأءכًٌٔ׋׸‬鵤ׁ‫׆׸‬ծ ،‫ٔف‬穄✪‫⥂דת‬䭯ׁ‫ׅתְת׃ג׸‬կ C#使用メモリ 未使用 C#用に確保したメモリ Unityメモリ アプリで使用している全体メモリ

8.

NativeContainer‫גְאח‬ ։GC ‫כה‬搀ꟼ⤘‫ז‬Memory然⥂։

9.

NativeContainer‫גְאח‬ • GC㼎韋‫ְז׵זח‬䕎‫׾ًٌٔד‬然⥂‫׷ׅ‬NativeArray‫؝ךוז‬ٝ‫ׅדشذ‬ • Allocate‫ה‬Dispose(C++‫ךד‬new‫ה‬delete‫ך‬ꟼ⤘)‫׾‬荈魦‫ד‬盖椚‫׸ֽז׃‬ ‫׿ׇתְֽל‬ • 䖓‫וק‬稱➜‫׷ׅ‬JobSystem/ECS瘝‫׵ַ♴׾‬佄ִ‫׷‬ꬊ䌢‫ח‬ꅾ銲‫ז‬堣腉‫ׅד‬ • unsafe‫ז‬C#‫ד‬剅ַ‫׭׋׷ְג׸‬ծ‫؝‬ٝ‫׾شذ‬倜鋉‫ח‬㹀纏‫׮הֿ׷ׅ‬〳腉 ‫ׅד‬

10.
[beta]
NativeContainer ‫ך‬⢪欽⢽
// 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();

11.
[beta]
NativeContainer ‫ך‬⢪欽⢽
// Vector3‫ך‬ꂁ⴨‫׾‬NativeArray‫ג׃ה‬然⥂‫ׅת׃‬
NativeArray<Vector3> array = new NativeArray<Vector3>(size, Allocator.Persistent);
// 兛鸐‫ך‬ꂁ⴨‫חֲ״ך‬⢪欽〳腉‫ׅד‬

int や float 等のプリミティブなタイプか、

for( int i = 0; i < array.Length; ++i ){

Vector3等のstruct型のみが指定可能です。

array[ i ] = array[i] + Vector3.one;
}

class型は指定不可です

// 然⥂‫׾ًٌٔ׋׃‬鍑佝‫ׅת׃‬կ
array.Dispose();

12.
[beta]
NativeContainer ‫ך‬⢪欽⢽
// Vector3‫ך‬ꂁ⴨‫׾‬NativeArray‫ג׃ה‬然⥂‫ׅת׃‬
NativeArray<Vector3> array = new NativeArray<Vector3>(size, Allocator.Persistent);
// 兛鸐‫ך‬ꂁ⴨‫חֲ״ך‬⢪欽〳腉‫ׅד‬

メモリの生存期間に合わせて、以下の三つの中から選択可能です。

for( int i = 0; i < array.Length; ++i ){

Allocator.Temp ( そのフレームのみ有効 )

array[ i ] = array[i] + Vector3.one;
}

Allocator.TempForJob( そのJob中のみ有効)
Allocator.Persistent( 解放するまで有効 )

// 然⥂‫׾ًٌٔ׋׃‬鍑佝‫ׅת׃‬կ
array.Dispose();

下に行くほど、メモリ確保のコストが高くなります。

13.

NativeContainer‫ך‬珏겲‫גְאח‬ • 2018.1.0b儗挿‫ך׮׷ְג׏Ⰵד‬ • NativeArray • NativeSlice ( NativeArrayַ‫♧׵‬鿇ⴖ‫׷《׶‬瘝‫ח‬⢪欽) • ➙䖓鷄⸇✮㹀 • NativeList • NativeHashMap • NativeMultiHashMap • NativeQueue ※ NativeContainer 自体は、unsafeなC#で書かれているため、 コンテナを新規に定義することも可能

14.

NativeContainer‫ד‬鍑佝䘌‫׵׋׸‬ Memory Leak‫כדך׷ׅ‬

15.

‫‪‬כדך׷ׅ‪MemoryLeak‬‬ ‫ׅת׃‪涪欰‬כؙ‪ծًٌٔٔ٦‬ה׷׸‪䘌‬ן‪ㄎ‬׾‪• Dispose‬‬ ‫ָؙ‪ծٔ٦‬גְג׏זחֲ״׷ׅ‪湊鋔‬׾ؙ‪ٔ٦‬כד‪ծEditor橆㞮‬׃׌׋ •‬ ‫ׅת⳿ָآ‪٦‬إحً‪ؒٓ٦‬ה׷ׅ‪涪欰‬‬ ‫׿ׇתְ‪遤‬כ‪湊鋔‬ؙ‪ծٔ٦‬׭׋ך‪鿪さ‬ך‪瘝‬أ‪ٝ‬و‪ؓ٦‬ؿػכד♳‪• 㹋堣‬‬

16.

C# JobSystem‫ד‬箢ㄤׁ‫㉏׷׸‬겗 • 僇爙涸‫ח‬Thread‫׾‬甧‫הְזג‬ծC#‫ָגⰋרקךزفؙٔأ‬MainThread♳ ‫ד‬㹋遤ׁ‫׷׸‬ • Unityָ甧‫׋ג‬WorkerThreadⰻ‫ؒכ‬ٝ‫آ‬ٝⰻ鿇‫Ⳣך‬椚㼔欽‫גְג׏זה‬ծ C#‫Ⳣךزفؙٔأ‬椚‫׾‬㹋遤‫⳿ָהֿ׷ׅ‬勻‫׋׃ד׿ׇת‬

17.

植㖈‫ך‬Thread崞欽朐屣 (1) C#スクリプトの処理は基本的に MainThread上で実行されます

18.

植㖈‫ך‬Thread崞欽朐屣(2) UnityではMainThreadだけが働いていて、 Worker ThreadはIdle状態になってしまい 何も仕事をしていない状態がよくあります

19.

C# JobSystem‫גְאח‬ ։⡭‫׷ְג׏‬WorkerThread‫׾‬⢪ְⴖ‫ך׭׋׷‬堣腉։

20.

C# JobSystem‫גְאח‬ • ؒٝ‫آ‬ٝⰻ鿇Ⳣ椚㼔欽‫ך‬WorkerThread‫׾‬C#⩎‫Ⱅ׮ח‬Ꟛ‫׃‬ծC#‫زفؙٔأ‬ Ⳣ椚‫׾‬WorkerThread‫ⴓ׮ח‬䬐ׁ‫ָהֿ׷ׇ‬〳腉‫ׅת׃ח‬ • C#‫כד⩎زفؙٔأ‬㹋遤ׁ‫ⰻְ׋ׇ‬㺁‫׾‬Job‫ג׃ה‬涫ꐮ‫׃‬ծ WorkerThread‫Ⳣ׮ח‬椚‫ⴓ׾‬䬐ׁ‫ָהֿ׷ׇ‬〳腉‫ׅד‬ • ‫׃׌׋‬MainThread ⟃㢩ַ‫׵‬㹋遤ׁ‫דך׷זח✲׷׸‬ծ‫ךו׿הק‬ UnityAPI‫׾‬ㄎ‫⳿ָ✲ׅ⳿ן‬勻‫׷זֻז‬

21.

C# JobSystem‫ⵃך‬欽䖓‫גְאח‬ Idle状態で遊んでいたWorker ThreadにもC#スクリプト処理を 分担させることで全体の負荷を下げる事につながります

22.

C# JobSystem‫ד‬ぢֻ✲ծぢַ‫✲ְז‬ • 1‫ٖؿ‬٦‫חⰻ⟃ي‬ ‫׷ת‬稢ְַ⡲噟‫׾‬䫎‫⵸׾ך׷־‬䲿‫ח‬⡲‫ׅתְג׸׵‬ • ぢֻ✲ • 醱侧‫زؙؑآـؔך‬刿倜Ⳣ椚‫⚛׾‬遤‫Ⳣג׃‬椚‫׷ׅ‬ • ぢַ‫✲ְז‬ • ‫؟‬٦‫غ‬٦‫ךה‬鸐⥋Ⳣ椚‫׾‬ꬊず劍‫Ⳣד‬椚‫׷ׅ‬ → 䖞勻鸐‫ ׶‬C#⩎‫ד‬Thread甧‫׷ג‬瘝‫ך‬䩛岀ָ葺ְ‫ׅד‬կ

23.

C# JobSystem‫ⵖךד‬秈 • C# Heap‫ךפ‬،‫⳿ָأإؙ‬勻‫ְז‬ • NativeContainer‫כֻ׃׮‬ծ‫ָؤ؎؟‬㔿㹀ׁ‫׷׸‬struct‫ַ׃‬،‫⳿ָأإؙ‬ 勻‫׿ׇת‬ • 婁‫ךו‬Unity API‫ךפ‬،‫⳿ָأإؙ‬勻‫ְז‬ • Mathٓ؎‫װٔٓـ‬ծJobSystem‫ח׭׋ך‬欽䠐ׁ‫׋׸‬API‫ⵃַ׃‬欽ָ⳿勻 ‫׿ׇת‬

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を発行してます JobHandle handle = job.Schedule(); // 発行したJobが完了するのを待ちます handle.Complete();

26.

⡦ַ㣐ֹ‫Ⳣז‬椚‫ ך׭׋׷ׅ׾‬IJob(1) // Jobの定義部分 Jobを定義するには、structを作成します。 struct MyJob : IJob{ // Jobに渡したいパラメーター Job実行に必要なデータをメンバーに持たせます。 public int param; // 実際に実行する処理 メンバーは int/float等のプリミティブな型もしくはstruct/ public void Execute( ){ int sum = 0; NativeArrayしか持てません。UnityEngine.Objectのような参照型のメ for( int i = 0 ; i < param ; ++ i){ sum += i ; } Debug.Log( “sum “ + sum ); ンバーは持つことが出来ません } } 実行そのものは Executeメソッドに記述します // MonoBehaviourのUpdate内等で実際にJobを発行するところ // Jobを作成します var job = new MyJob(){ param = 100 }; // Jobを発行してます JobHandle handle = job.Schedule(); // 発行したJobが完了するのを待ちます handle.Complete();

27.

⡦ַ㣐ֹ‫Ⳣז‬椚‫ ך׭׋׷ׅ׾‬IJob(2) // 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(){ structをnewして、Schedule()メソッドを呼び出すこと param = 100 で、Jobの発行が出来ます。 }; // Jobを発行してます Scheduleからの返り値のJobHandleオブジェクトで発 JobHandle handle = job.Schedule(); // 発行したJobが完了するのを待ちます 行したJobを待たせることが出来ます handle.Complete();

28.

ꂁ⴨‫ך‬ぐ銲稆⽃⡘‫⚛ד‬遤‫Ⳣג׃‬椚‫׾‬遤ֲ‫ך׭׋‬ IJobParalellFor NativeArray< Vector3 > position [0] [1] [2] [3] [4] ... ( 0, 0, 10 ) ( 0, 0, 20 ) ( 0, 0, 30 ) ( 0, 0, 40 ) ( 0, 0, 50 ) ... これに対して、( 0,0,1 )を足していく ( 0, 0, 11) ( 0, 0, 21 ) ( 0, 0, 31 ) ( 0, 0, 41 ) IJobParallelForを使えば、この 「(0,0,1) を足す作業」を 平行して処理させることが出来ます。 ( 0, 0, 51 )

29.

ⴓ侔䖓‫Ⳣך‬椚؎ً٦‫آ‬ 分散前 MainThread position [ 0~30 ]の更新処理 分散後 MainThread Jobの発行及び完了待ち position [ 0~10 ]の更新処理 Worker position [ 11~20 ]の更新処理 Worker position [ 21~30 ]の更新処理

30.
[beta]
ꂁ⴨‫ך‬ぐ銲稆⽃⡘‫⚛ד‬遤‫Ⳣג׃‬椚‫׾‬遤ֲ‫ך׭׋‬
IJobParalellFor
// 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で良い感じに設定してくれる)
JobHandle handle = job.Schedule( positions.Length ,0 );
// 発行したJobが完了するのを待ちます
handle.Complete();

31.
[beta]
ꂁ⴨‫ך‬ぐ銲稆⽃⡘‫⚛ד‬遤‫Ⳣג׃‬椚‫׾‬遤ֲ‫ך׭׋‬
IJobParalellFor
// Jobの定義部分
struct MyPositionUpdate : IJobParallelFor{
// 更新対象をNativeArrayの形で保持します
public NativeArray<Vector3> positions;
public float deltaTime;
// 実行部分 indexは、更新対象をする配列のindex値が入ります。
public void Execute( int index ){
更新の対象は、Nativeコンテナである必要があります。
positions[
index ] = positions[ index ] + Vector3.up * deltaTime;
}
}

// MonoBehaviourのUpdate内等で実際にJobを発行するところ
// Jobを作成します
var job = new MyPositionUpdate(){
positions = bulletPositions, // <- NativeArray<Vector3> bulletPositions;が定義されているつもり
deltaTime = Time.deltaTime ,//<- Main以外からはTime.deltaTimeが呼べないので…
};
// Jobを発行してます。対象の配列要素数・バッチ数を指定(0で良い感じに設定してくれる)
JobHandle handle = job.Schedule( positions.Length ,0 );
// 発行したJobが完了するのを待ちます
handle.Complete();

32.
[beta]
ꂁ⴨‫ך‬ぐ銲稆⽃⡘‫⚛ד‬遤‫Ⳣג׃‬椚‫׾‬遤ֲ‫ך׭׋‬
IJobParalellFor
// 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;
}
}

予期せぬエラーを防ぐため、引数で渡された「index」以外に
// MonoBehaviourのUpdate内等で実際にJobを発行するところ
//書き込もうとすると、Editor上ではエラーメッセージが出るよ
Jobを作成します
var
job = new MyPositionUpdate(){
うになっています。
positions = bulletPositions, // <- NativeArray<Vector3> bulletPositions;が定義されているつもり
deltaTime = Time.deltaTime ,//<- Main以外からはTime.deltaTimeが呼べないので…
};
// Jobを発行してます。対象の配列要素数・バッチ数を指定(0で良い感じに設定してくれる)
JobHandle handle = job.Schedule( positions.Length ,0 );
// 発行したJobが完了するのを待ちます
handle.Complete();

33.

複数のTransformに対して並行して処理を行うための IJobParallelForTransform IJobParallelForTransformを利用することで、このCubeの移 動や回転をWorkerThreadでもできるようになります。 (通常だと transformへの書き込みはMainThread以外からでは できませんが、 IJobParallelForTransformの TransformAccess経由ならば可能です)

34.
[beta]
複数のTransformに対して並行して処理を行うための
IJobParallelForTransform
// Jobの定義部分
struct MyTransformUpdateJob : IJobParallelForTransform{
// Jobに渡したいパラメーター
public int objNum;
public float time;
// 実際に実行する処理 indexには配列の何個目であるか、transform に対して値をセットします
public void Execute(int index, TransformAccess transform){
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を発行して、直後で完了待ち
JobHandle handle = myTransformUpdateJob.Schedule( transformAccessArray );
handle.Complete();

35.
[beta]
複数のTransformに対して並行して処理を行うための
IJobParallelForTransform
// Jobの定義部分
struct MyTransformUpdateJob : IJobParallelForTransform{
// Jobに渡したいパラメーター
public int objNum;
public float time;
// 実際に実行する処理 indexには配列の何個目であるか、transform に対して値をセットします
public void Execute(int index, TransformAccess transform){
transform.position = new Vector3( index – objNum / 2 , time , 0.0f);
}
引数で渡ってくるTransformAccess経由でpositionやrotationを
}

設定します。

// MonoBehaviourのUpdate内等で実際にJobを発行するところ
Transform[] transformArray = "xxx";
// 並行して処理をしたいTransformのリストを定義します
var transformAccessArray = new TransformAccessArray(transformArray);
// Jobを作成します
var myTransformUpdateJob = new MyTransformUpdateJob(){
time = Time.deltaTime , objNum = transformArray.Length
};
// Jobを発行して、直後で完了待ち
JobHandle handle = myTransformUpdateJob.Schedule( transformAccessArray );
handle.Complete();

36.

Job‫ⵃ׾يذأء‬欽‫׋׃‬㼔欽API • 植㖈JobSystem‫ⵃ׾‬欽‫׋׃‬API ‫♴כ‬鎸 • 醱侧‫ך‬Raycast‫׾‬Job‫ⵃ׾‬欽‫⚛ג׃‬遤‫ג׃‬㹋遤‫׷ׅ‬RayCastCommand • NaviMesh‫ٕך‬٦‫ز‬嗚稊‫׾‬Job‫⚛ג׃ח‬遤‫ג׃‬㹋遤‫׷ׅ‬NavMeshQuery

37.

C JobSystem‫׾‬剣⸬崞欽‫ה׷ׅ‬ծ ‫ٔذحغ‬٦嶊顤‫׮‬䫇ִ‫ׅת׸׵‬

38.

㹋꿀‫׋׫ג׃‬穠卓 https://github.com/wotakuro/UnityJobSystemTest こちらで計測。GPU・通信の影響を抑えるため、機内モード/画面輝度最低固定、ゲームのFPSを10固定 にし、CPUでのバッテリー消費が顕著に出る環境にして実験。 Android / iOS共に、JobSystemにして処理時間が減った結果結果、CPUの休む時間が増えたり、MainThreadへの負荷が減るので CPUの実行周波数を下げることが出来た等で、バッテリー消費が下がったと思われる。

39.

C# JobSystem ‫ⵖך‬䖴‫גְאח‬

40.

C# JobSystem‫ⵖך‬䖴‫גְאח‬ QJob‫ח‬Priority(⮚⯓䏝)‫⳿כֽב‬勻‫ַׅת‬ APriority‫ָ׿ׇת׶֮כ‬ծJob꟦‫ךד‬⣛㶷ꟼ⤘‫כ‬鏣㹀‫ׅתֹד‬ Job B‫כ‬ծJob A‫ח‬⣛㶷‫ה׷ׅ‬鏣㹀‫׋׃‬㜥さծJob Bָ㸣✪‫דת׷ׅ‬Job A‫כ‬ 㹋遤ׁ‫׿ׇת׸‬

41.

WorkerThread‫ך‬侧‫גְאח‬

42.

WorkerThread‫ך‬侧‫גְאח‬ WorkerThreadの数はプラットフォーム・マシンの ハードウェアによって変わってきます。 多くのプラットフォームでは、コア数 – 1 だけ WorkerThreadに割り当てされます

43.

Job㹋遤儗‫ٓؒך‬٦‫גְאח‬

44.

Job㹋遤儗‫ٓؒך‬٦‫גְאח‬ Job.Schedule時に、InvalidOperationException等が出てしまうことがあります。 これはJobの実行タイミングで結果が異なりうる時にエラーとなってしまう可 能性があるのをUnityEditorが察知してエラーを出しています。 異なる二つのJobが同じ参照先のデータを有していた場合等に発生します。

45.

Job㹋遤儗‫ٓؒך‬٦‫גְאח‬ Worker Worker Job A positions Job B positions NativeArray< Vector3 > positions [0] [1] [2] [3] [4] ... ( 0, 0, 10 ) ( 0, 0, 20 ) ( 0, 0, 30 ) ( 0, 0, 40 ) ( 0, 0, 50 ) ...

46.

Job㹋遤儗‫ٓؒך‬٦‫גְאח‬ Worker Job A positions 同時に走っているJobが同じ参照先( NativeContainer )を指 していた場合に複数のJobから同一のリソース書き込みを Worker Job B される可能性を考慮してEditor実行時にはエラーを出すよ positions うにしています。 NativeArray< Vector3 > positions [0] [1] [2] [3] [4] ... ( 0, 0, 10 ) ( 0, 0, 20 ) ( 0, 0, 30 ) ( 0, 0, 40 ) ( 0, 0, 50 ) ...

47.
[beta]
Job㹋遤儗‫ٓؒך‬٦‫גְאח‬
Worker

Job A

positions

書き込みを行わないのであれば、Jobの変数宣言に[ReadOnly] 属性をつけて、
このpositionsへの書き込みは行わない事を明示的に教えます。

Worker

struct MyJob : IJob{

Job B

positions

[ReadOnly]
public NativeArray<Vector3> positions;
}
また[ReadOnly]の反対で、[WriteOnly]属性もあります。

NativeArray< Vector3 > positions
[0]

[1]

[2]

[3]

[4]

...

( 0, 0, 10 )

( 0, 0, 20 )

( 0, 0, 30 )

( 0, 0, 40 )

( 0, 0, 50 )

...

48.

IJobParallelFor 㹋遤儗‫ٓؒך‬٦‫גְאח‬ Job.Schedule実行中に IndexOutOfRangeExceptionが出てきました。 しかし配列外のアクセスには心当たりがありません…。 とりあえず、ソースコードを見てみましょう…

49.
[beta]
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.
[beta]
IJobParallelFor 㹋遤儗‫ٓؒך‬٦‫גְאח‬
// Jobの定義部分
ここで実行時にエラーが出ます。
struct MyPositionUpdate : IJobParallelFor{
これは velocityByEnemyType[ enemyType ]と、引数「index」以外にアクセスし
// 更新対象をNativeArrayの形で保持します
public NativeArray<Vector3> positions;
ているためです。
// 敵のタイプ別の速度
public NativeArray<Vector3> velocityByEnemyType;
アクセスがReadなのかWriteなのかまでは判別は判別できませんが、指定され
// 敵のタイプ指定
たindex以外にアクセスが起きている事だけは UnityEditor側で検知が出来ます。
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;
}
}

51.
[beta]
IJobParallelFor 㹋遤儗‫ٓؒך‬٦‫גְאח‬
// Jobの定義部分
struct MyPositionUpdate : IJobParallelFor{
// 更新対象をNativeArrayの形で保持します
public NativeArray<Vector3> positions;
// 敵のタイプ別の速度
[ReadOnly]
public NativeArray<Vector3> velocityByEnemyType;
// 敵のタイプ指定
public NativeArray<int> enemyType;
velocityByEnemyTypeに対して [ReadOnly]属性をつけてあげる事で、
// deltaTIme
public float deltaTime;
velocityByEnemyTypeへの書き込みを行わないことを明示する事で実行時エラーが
// 実行部分 indexは、更新対象をする配列のindex値が入ります。
回避できます。
public void Execute( int index ){
int enemyType = enemyType[index];
Vector3 velocity = velocityByEnemyType[ enemyType ];
positions[ index ] = positions[ index ] + Vector3.front * deltaTime;
}
}

52.

C# JobSystem‫ؙحصؙذךד‬

53.

如Update‫⯓ך‬걧‫ד‬㸣✪䖉‫׶״דהֿ׷ׅ׾׍‬넝鸞⻉‫ׅת׃‬ class JobTest : MonoBehaviour{ private JobHandle handle; 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に積まれるだけです JobHandle handle = myTransformUpdateJob.Schedule( transformAccessArray ); // [重要] 実際に積まれたJobの実行を促します JobHandle.ScheduleBatchedJobs(); } }

54.

如Update‫⯓ך‬걧‫ד‬㸣✪䖉‫׶״דהֿ׷ׅ׾׍‬넝鸞⻉‫ׅת׃‬ MainThread Jobの発行及び完了待ち エンジン側での描画等の更新処理 EngineのJob Job更新処理 Worker Jobの更新処理 EngineのJob Worker Jobの更新処理 EngineのJob エンジンの処理の隙間にユーザーの処理を埋める 事で全体での処理時間を縮める事が可能です MainThread 完了 Job 待ち 発行 エンジン側での描画等の更新処理 EngineのJob EngineのJob Worker Jobの更新処理 EngineのJob Worker Jobの更新処理 Jobの更新処理

55.

闌纏䕎䒭‫דת؝؝כ‬ ֮‫כה‬㹋騧箟

56.

劤傈‫ך‬怴统‫גְאח‬ Work1.IJob Work3.IJobParallelForTransform Work2.IJobParallelFor Work4.RayCastCommand