并行化job

ParallelFor jobs​docs.unity3d.com

当调度Jobs时,只能有一个job来进行一项任务。在游戏中,非常常见的情况是在一个庞大数量的对象上执行一个相同的操作。这里有一个独立的job类型叫做IJobParallelFor来处理此类问题。ParallelFor jobs当调度Jobs时,只能有一个job来进行一项任务。在游戏中,非常常见的情况是在一个庞大数量的对象上执行一个相同的操作。这里有一个独立的job类型叫做IJobParallelFor来处理此类问题。

注意:“并行化”job是Unity中所有实现了IJobParallelFor接口的结构的总称。

一个并行化job使用一个NativeArray存放数据来作为它的数据源。并行化job横跨多个核心执行。每个核心上有一个job,每个job处理一部分工作量。IJobParallelFor的行为很类似于IJob,但是不同于只执行一个Execute方法,它会在数据源的每一项上执行Execute方法。Execute方法中有一个整数型的参数。这个索引是为了在job的具体操作实现中访问和操作数据源上的单个元素。

一个定义并行化Job的例子:

struct IncrementByDeltaTimeJob: IJobParallelFor
{public NativeArray<float> values;public float deltaTime;public void Execute (int index){float temp = values[index];temp += deltaTime;values[index] = temp;}
}

调度并行化job

当调度并行化job时,你必须指定你分割NativeArray数据源的长度。在结构中同时存在多个NativeArrayUnity时,C# Job System不知道你要使用哪一个NativeArray作为数据源。这个长度同时会告知C# Job System有多少个Execute方法会被执行。

在这个场景中,并行化job的调度会更复杂。当调度并行化任务时,C# Job System会将工作分成多个批次,分发给不同的核心来处理。每一个批次都包含一部分的Execute方法。随后C# Job System会在每个CPU核心的Unity原生Job System上调度最多一个job,并传递给这个job一些批次的工作来完成。

一个并行化job划分批次到多个CPU核心

当一个原生job提前完成了分配给它的工作批次后,它会从其他原生job那里获取其剩余的工作批次。它每次只获取那个原生job剩余批次的一半,为了确保缓存局部性(cache locality)。

为了优化这个过程,你需要指定一个每批次数量(batch count)。这个每批次数量控制了你会生成多少job和线程中进行任务分发的粒度。使用一个较低的每批次数量,比如1,会使你在线程之间的工作分配更平均。它会带来一些额外的开销,所以有时增加每批次数量会是更好的选择。从每批次数量为1开始,然后慢慢增加这个数量直到性能不再提升是一个合理的策略。

调度并行化job的例子:

job代码

// Job adding two floating point values together
public struct MyParallelJob : IJobParallelFor
{[ReadOnly]public NativeArray<float> a;[ReadOnly]public NativeArray<float> b;public NativeArray<float> result;public void Execute(int i){result[i] = a[i] + b[i];}
}

主线程代码:

NativeArray<float> a = new NativeArray<float>(2, Allocator.TempJob);NativeArray<float> b = new NativeArray<float>(2, Allocator.TempJob);NativeArray<float> result = new NativeArray<float>(2, Allocator.TempJob);a[0] = 1.1;
b[0] = 2.2;
a[1] = 3.3;
b[1] = 4.4;MyParallelJob jobData = new MyParallelJob();
jobData.a = a;
jobData.b = b;
jobData.result = result;// Schedule the job with one Execute per index in the results array and only 1 item per processing batch
JobHandle handle = jobData.Schedule(result.Length, 1);// Wait for the job to complete
handle.Complete();// Free the memory allocated by the arrays
a.Dispose();
b.Dispose();
result.Dispose();

ParallelForTransform jobs

https://docs.unity3d.com/Manual/JobSystemParallelForTransformJobs.html

一个ParallelForTransform job是另一个类型的ParallelFor job;它是专门为了Transforms上的操作设计的。

注意:ParallelForTransform job是Unity中所有实现了IJobParallelForTransform接口的结构的总称。

C# Job System建议和故障排除

C# Job System tips and troubleshooting​docs.unity3d.com

当你使用Unity C# Job System时,确保你遵守以下几点:C# Job System tips and troubleshooting当你使用Unity C# Job System时,确保你遵守以下几点:

不要从一个job中访问静态的数据

在所有的安全性系统中你都应当避免从一个job中访问静态数据。如果你访问了错误的数据,你可能会使Unity崩溃,通常是以意想不到的方式。举例来说,访问一个MonoBehaviour可以导致域重新加载时崩溃。

注意:因为这个风险,未来版本的Unity会通过静态分析来阻止全局变量在job中的访问。如果你确实在job中访问了静态数据,你应当预见到你的代码会在Unity未来的版本中报错。

刷新已调度的批次

当你希望你的job开始执行时,你可以通过JobHandle.ScheduleBatchedJobs来刷新已调度的批次。注意调用这个接口时会对性能产生负面的影响。不刷新批次将会延迟调度job,直到主线程开始等待job的结果。在任何其他情况中,你应当调用JobHandle.Complete来开始执行过程。

注意:在Entity Component System(ECS)中批次会暗中为你进行刷新,所以调用JobHandle.ScheduleBatchedJobs是不必要的。

不要试图去更新NativeContainer的内容

由于缺乏引用返回值,不可能去直接修改一个NativeContainer的内容。例如,nativeArray[0]++ ;和 var temp = nativeArray[0]; temp++;一样,都没有更新nativeArray中的值。

你必须从一个index将数据拷贝到一个局部临时副本,修改这个副本,并将它保存回去,像这样:

MyStruct temp = myNativeArray[i];
temp.memberVariable = 0;
myNativeArray[i] = temp;

调用JobHandle.Complete来重新获得归属权

在主线程重新使用数据前,追踪数据的所有权需要依赖项都完成。只检查JobHandle.IsCompleted是不够的。你必须调用JobHandle.Complete来在主线程中重新获取NaitveContainer类型的所有权。调用Complete同时会清理安全性系统中的状态。不这样做的话会造成内存泄漏。这个过程也在你每一帧都调度依赖于上一帧job的新job时被采用。

在主线程中调用Schedule和Complete

你只能在主线程中调用ScheduleComplete方法。如果一个job需要依赖于另一个,使用JobHandle来处理依赖关系而不是尝试在job中调度新的job。

在正确的时间调用Schedule和Complete

一旦你拥有了一个job所需的数据,尽可能快地在job上调用Schedule,在你需要它的执行结果之前不要调用Complete。一个良好的实践是调度一个你不需要等待的job,同时它不会与当前正在运行的其他job产生竞争。举例来说,如果你在一帧结束和下一帧开始之前拥有一段没有其他job在运行的时间,并且可以接受一帧的延迟,你可以在一帧结束的时候调度一个job,在下一帧中使用它的结果。或者,如果这个转换时间已经被其他job占满了,但是在一帧中有一大段未充分利用的时段,在这里调度你的job会更有效率。

将NativeContainer标记为只读的

记住job在默认情况下拥有NativeContainer的读写权限。在合适的NativeContainer上使用[ReadOnly]属性可以提升性能。

检查数据的依赖

在Unity的Profiler窗口中,主线程中的"WaitForJobGroup"标签表明了Unity在等待一个工人线程上的job结束。这个标签可能意味着你以某种方式引入了一个资源依赖,你需要去解决它。查找JobHandle.Complete来追踪你在什么地方有资源依赖,导致主线程必须等待。

调试job

job拥有一个Run方法,你可以用它来替代Schedule从而让主线程立刻执行这个job。你可以使用它来达到调试目的。

不要在job中开辟托管内存

在job中开辟托管内存会难以置信得慢,并且这个job不能利用Unity的Burst编译器来提升性能。Burst是一个新的基于LLVM的后端编译器技术,它会使事情对于你更加简单。它获取C# job并利用你平台的特定功能产生高度优化的机器码。

更多信息

  • 观看Unity GDC 2018: C# Job System的片段列表
  • 获取C# Job Syetem与ECS交互的更进一步信息,查看ECS package documentation on GitHub

Unity C# Job System介绍(四) 并行化Job和故障排除(完结)相关推荐

  1. Unity C# Job System介绍(二) 安全性系统和NativeContainer

    C# Job System中的安全性系统 https://docs.unity3d.com/Manual/JobSystemSafetySystem.html​docs.unity3d.com 资源竞 ...

  2. Unity C# Job System介绍(一) Job System总览和多线程

    C# Job System 总览 Unity的C# Job System使用户可以编写与Unity其他部件交互的多线程代码,同时让编写正确的代码变得更容易. 编写多线程代码可以提供更好的性能表现.这包 ...

  3. Unity C# Job System介绍(三) Job的创建和调度

    创建Jobs Unity - Manual: Creating jobs​docs.unity3d.com 为了在Unity中创建一个job你需要实现IJOb接口.IJob允许你调度一个job,和其他 ...

  4. Unity 游戏黑暗之光笔记第四章 任务系统的实现

    Unity 游戏黑暗之光笔记 第四章 任务系统的实现 具体步骤可以看RPG游戏<黑暗之光>流程介绍与代码分析之(四) 注意要点 在把鼠标放在任务面板上点击时,为了让角色不移动,在Playe ...

  5. Unity中的shadows(四)collect shadows

    Unity中的shadows(四)collect shadows 阴影收集 vertex shader frag_hard frag_pcfSoft Reference 本文是Unity中的shado ...

  6. 【Unity/Tutorial】官方AircraftJetAI(四)喷射机首飞

    目录 说在前面 系列目录 相关函数 移动喷射机 添加刚体/BoxCollier组件 控制器脚本 用户控制脚本 飞行录像 说在前面 Unity版本:2017.4.37 资源下载:这个资源是官方自带的资源 ...

  7. Unity4.x 2D游戏开发基础教程第1章Unity及其组成的介绍

    Unity4.x 2D游戏开发基础教程第1章Unity及其组成的介绍 本书主要讲解的是,如何使用Unity开发2D游戏.但在开始讲解之前,最好先熟悉一下Unity这个工具.本章会首先介绍Unity的下 ...

  8. 原来在UNITY中使用system.io下的所有函数都可以用相对路径 : Assets/xx

    原来在UNITY中使用system.io下的所有函数都可以用相对路径 : Assets/xx 代码如下图,这样就不用在绝对路径和相对路径之间不断转换了. 想要得到绝对路径时就傅 Application ...

  9. 学计算机学生笔记本电脑实用,介绍四款适合学生党的笔记本电脑

    开一年一季的开学季又到了,恭喜的莘莘学子经过十年寒窗,终于考上了自己理想的大学,对于没有考上的学子你也不要气馁,毕竟过去不是炫耀未来的资本,未来还需努力.进入大学很多学生及其家长都在犹豫要不要在大一入 ...

最新文章

  1. 面试官问:Integer 如何实现节约内存和提升性能的?
  2. 站长之家html视频播放,HTML5视频发展状况
  3. 敏捷项目管理—敏捷四宣言
  4. oracle如何清空用户信息,Oracle数据库如何创建和删除用户
  5. 用redis+jwt保存在线用户和获得在线用户列表、踢出用户示例
  6. Hotspot GC Root 对应调用链
  7. 时下流行的9种恶意软件,你都了解吗?
  8. 使用CDI简化JAX-RS缓存
  9. String,StringBuffer,StringBuilder简单对比
  10. [react] 可以使用TypeScript写React应用吗?怎么操作?
  11. 计算机学院优秀主讲教师评选细则,关于转发《华南师范大学第二届“我最喜爱的导师”评选活动实施细则》的通知...
  12. Axure自动幻灯片制作
  13. 金融交易学——一个专业交易者…
  14. 【图算法】(3) 网络的基本静态几何特征(二),附networkx完整代码
  15. 基于 MATLAB 的图像中汉字识别
  16. 从头来过教你PHP脚本语言(先导篇)
  17. 网易新闻回答2021:靠差异化内容逆势增长
  18. 硅二极管温度传感器的特点
  19. lumen php版本,PHP微框架 Lumen 使用全纪录
  20. 微信小程序全局数据共享学习笔记

热门文章

  1. java多线程的join方法_java多线程系列_join方法的使用(5)
  2. 软件包管理器(树链剖分)
  3. CIKM 2021 | 基于异质图学习的搜索广告关键词推荐
  4. 十进制转二进制转换c语言程序,c语言编程如何实现-十进制数转换成二进制和 – 手机爱问...
  5. ajax绑定事件页面重复提交,ajax防止用户重复提交点击事件
  6. wifi rssi 计算 距离_WiFi和WLAN是一样的?真相在这里
  7. 电脑小写字母怎么切换_电脑上也可以使用九州音集啦!
  8. 【图像超分辨率】RRSGAN: Reference-Based Super-Resolution for Remote Sensing Image
  9. python如何遍历文件夹中的所有图片_python实现遍历文件夹图片并重命名
  10. 数学--数论--容斥定理完全解析(转)