| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> 游戏开发 -> unity job系统学习 -> 正文阅读 |
|
[游戏开发]unity job系统学习 |
目录
前言为什么要学:因为想要知道怎么写这些代码的GPU版本 想要看懂大佬的代码 什么是多线程默认情况下,一个线程在程序开始时运行。这是“主线”。主线程创建新线程来处理任务。这些新线程彼此并行运行,并且通常在完成后将其结果与主线程同步。 然而,游戏开发代码通常包含许多要一次执行的小指令。如果您为每个instruction创建一个线程,您最终会得到许多线程,每个线程的生命周期都很短。这可能会突破 CPU 和操作系统的处理能力极限。 可以通过拥有一个线程池来缓解线程生命周期的问题。但是,即使您使用线程池,您也很可能同时有大量线程处于活动状态。线程多于 CPU 内核会导致线程相互竞争 CPU 资源,从而导致频繁的上下文切换。上下文切换是在执行过程中保存线程状态的过程,然后在另一个线程上工作,然后重建第一个线程,稍后继续处理它。上下文切换是资源密集型的,因此您应该尽可能避免使用它。 什么是job systemA job system manages multithreaded code by creating jobs instead of threads. A job system manages a group of worker threads across multiple cores. It usually has one worker thread per logical CPU core, to avoid context switching (although it may reserve some cores for the operating system or other dedicated applications). A job system puts jobs into a job queue to execute. Worker threads in a job system take items from the job queue and execute them. A job system manages dependencies and ensures that jobs execute in the appropriate order. 什么是一个jobA job is a small unit of work that does one specific task. A job receives parameters and operates on data, similar to how a method call behaves. Jobs can be self-contained, or they can depend on other jobs to complete before they can run. 什么是job dependenciesIn complex systems, like those required for game development, it is unlikely that each job is self-contained. One job is usually preparing the data for the next job. Jobs are aware of and support dependencies to make this work. If jobA has a dependency on jobB, the job system ensures that jobA does not start executing until jobB is complete. C# Job System的安全系统Race conditionsA race condition occurs when the output of one operation depends on the timing of another process outside of its control. A race condition is not always a bug, but it is a source of nondeterministic behavior. When a race condition does cause a bug, it can be hard to find the source of the problem because it depends on timing, so you can only recreate the issue on rare occasions. Debugging it can cause the problem to disappear, because breakpoints and logging can change the timing of individual threads. Race conditions produce the most significant challenge in writing multithreaded code. Safety systemthe Unity C# Job System detects all potential race conditions and protects you from the bugs they can cause. 比如说,如果C# job system在一个job的control thread中从你的代码发送了一个数据的referencee,它无法确定在job正在写data的同时是否control thread也在读data。这种情况造成了一个竞争。 The way the C# Job System copies data means that a job can only access blittable data types. These types do not need conversion when passed between managed and native code. The C# Job System can copy blittable types with memcpy and transfer the data between the managed and native parts of Unity. It uses memcpy to put data into native memory when scheduling jobs and gives the managed side access to that copy when executing jobs. For more information, see Scheduling jobs. Native Container安全系统复制数据过程的缺点是它还会隔离每个副本中的作业结果。为了克服这个限制,您需要将结果存储在一种称为NativeContainer的共享内存中。 NativeContainer是一种托管值类型,可为本机内存提供安全的 C# wrapper 。它包含一个指向unmanaged allocation的指针。当与 Unity C# job 系统一起使用时,NativeContainer允许job访问与主线程共享的数据,而不是使用副本。 NativeContainer 的类型Unity 附带了一个NativeContainer名为NativeArray。您还可以使用 NativeSlice 操作 aNativeArray以获取从NativeArray特定位置到特定长度的子集。 注意:实体组件系统(ECS) 包扩展了Unity.Collections命名空间以包括其他类型NativeContainer: NativeList- 可调整大小的NativeArray. NativeContainer 和安全系统NativeContainer类型中。它跟踪任何正在读取和写入的内容NativeContainer。 注意:所有类型的安全检查NativeContainer(例如越界检查、释放检查和竞争条件检查)仅在 Unity编辑器和播放模式中可用。 该安全系统的一部分是DisposeSentinel和AtomicSafetyHandle。DisposeSentinel如果您没有正确释放内存,它会检测内存泄漏并给您一个错误。触发内存泄漏错误发生在泄漏发生很久之后。 使用AtomicSafetyHandle转移NativeContainer代码中的所有权。例如,如果两个 scheduled jobs正在写入同一个NativeArray,安全系统会抛出异常,并带有明确的错误消息,解释为什么以及如何解决问题。当您schedule the offending job时,安全系统会引发此异常。 在这种情况下,您可以使用依赖. 第一个 job可以写入NativeContainer,一旦它完成执行,下一个作业就可以安全地读取和写入相同的NativeContainer . 当从主线程访问数据时,读取和写入限制也适用。安全系统确实允许多个job并行读取相同的数据。 默认情况下,当job可以访问 NativeContainer时,它同时具有读取和写入访问权限。此This configuration can slow performance. The C# Job System doesn’t allow you to schedule a job that has write access to a NativeContainer at the same time as another job that’s writing to it. If a job doesn’t need to write to a NativeContainer, mark the NativeContainer with the [ReadOnly] attribute, like so:
In the above example, you can execute the job at the same time as other jobs that also have read-only access to the first NativeArray. Note: There is no protection against accessing static data from within a job. Accessing static data circumvents all safety systems and can crash Unity. For more information, see C# Job System tips and troubleshooting. NativeContainer AllocatorWhen creating a NativeContainer, you must specify the memory allocation type that you need. The allocation type depends on the length of time the job runs. This way you can tailor the allocation to get the best performance possible in each situation. There are three Allocator types for NativeContainer memory allocation and release. You must specify the appropriate one when instantiating a NativeContainer. Allocator.Temp has the fastest allocation. Use it for allocations with a lifespan of one frame or fewer. However, you can’t use Temp to pass NativeContainer allocations to jobs. For example:
Note: The number 1 in the example above indicates the size of the NativeArray. In this case, it has only one array element because it only stores one piece of data in result. 创建jobTo create a job in Unity you need to implement the IJob interface. IJob allows you to schedule a single job that runs in parallel to any other jobs that are running. Note: A “job” is a collective term in Unity for any struct that implements the IJob interface. To create a job, you need to:
When executing the job, the Execute method runs once on a single core. Note: When designing your job, remember that they operate on copies of data, except in the case of NativeContainer. So, the only way to access data from a job in the control thread is by writing to a NativeContainer. An example of a simple job definition
调度jobsTo schedule a job, you must:
Calling Schedule puts the job into the job queue for execution at the appropriate time. Once scheduled, you cannot interrupt a job. Note: You can not call Schedule from within jobs. An example of scheduling a job
我应该什么时候dispose这些变量 JobHandle and dependenciesWhen you call the Schedule method of a job it returns a JobHandle. You can use a JobHandle in your code as a dependency for other jobs. If a job depends on the results of another job, you can pass the first job’s JobHandle as a parameter to the second job’s Schedule method, like so:
Note: All of a job’s dependencies must be scheduled on the same control thread as the job itself. Combining dependenciesIf a job has many dependencies, you can use the method JobHandle.CombineDependencies to merge them. CombineDependencies allows you to pass them onto the Schedule method.
Waiting for jobs in the control threadUse JobHandle to force your code to wait in the control thread for your job to finish executing. To do this, call the method Complete on the JobHandle. At this point, you know the control thread can safely access the NativeContainer that the job was using. Note: Jobs do not start executing when you schedule them. If you are waiting for the job in the control thread, and you need access to the NativeContainer data that the job is using, you can call the method JobHandle.Complete. This method flushes the jobs from the memory cache and starts the process of execution. Calling Complete on a JobHandle returns ownership of that job’s NativeContainer types to the control thread. You need to call Complete on a JobHandle to safely access those NativeContainer types from the control thread again. It is also possible to return ownership to the control thread by calling Complete on a JobHandle that is from a job dependency. For example, you can call Complete on jobA, or you can call Complete on jobB which depends on jobA. Both result in the NativeContainer types that are used by jobA being safe to access on the control thread after the call to Complete. Otherwise, if you don’t need access to the data, you need to explicity flush the batch. To do this, call the static method JobHandle.ScheduleBatchedJobs. Note that calling this method can negatively impact performance. An example of multiple jobs and dependenciesJob code:
Main thread code:
ParallelFor jobsWhen scheduling jobs, there can only be one job doing one task. In a game, it is common to want to perform the same operation on a large number of objects. There is a separate job type called IJobParallelFor to handle this. A ParallelFor job uses a NativeArray of data to act on as its data source. ParallelFor jobs run across multiple cores. There is one job per core, each handling a subset of the workload. IJobParallelFor behaves like IJob, but instead of a single Execute method, it invokes the Execute method once per item in the data source. There is an integer parameter in the Execute method. This index is to access and operate on a single element of the data source within the job implementation. An example of a ParallelFor job definition:
Scheduling ParallelFor jobsWhen scheduling ParallelFor jobs, you must specify the length of the NativeArray data source that you are splitting. The Unity C# Job System cannot know which NativeArray you want to use as the data source if there are several in the struct. The length also tells the C# Job System how many Execute methods to expect. Behind the scenes, the scheduling of ParallelFor jobs is more complicated. When scheduling ParallelFor jobs, the C# Job System divides the work into batches to distribute between cores. Each batch contains a subset of Execute methods. The C# Job System then schedules up to one job in Unity’s native job system per CPU core and passes that native job some batches to complete.
To optimize the process, you need to specify a batch count. The batch count controls how many jobs you get, and how fine-grained the redistribution of work between threads is. Having a low batch count, such as 1, gives you a more even distribution of work between threads. It does come with some overhead, so sometimes it is better to increase the batch count. Starting at 1 and increasing the batch count until there are negligible performance gains is a valid strategy. An example of scheduling a ParallelFor jobJob code:
Control thread code:
ParallelForTransform jobsA ParallelForTransform job is another type of ParallelFor job; designed specifically for operating on Transforms. C# Job System tips and troubleshootingWhen using the Unity C# Job System, make sure you adhere to the following: Do not access static data from a jobAccessing static data from a job circumvents all safety systems. If you access the wrong data, you might crash Unity, often in unexpected ways. For example, accessing MonoBehaviour can cause crashes on domain reloads. Flush scheduled batchesWhen you want your jobs to start executing, then you can flush the scheduled batch with JobHandle.ScheduleBatchedJobs. Note that calling this method can negatively impact performance. Not flushing the batch delays the scheduling until the control thread waits for the result. In all other cases use JobHandle.Complete to start the execution process. Note: In the Entity Component System (ECS) the batch is implicitly flushed for you, so calling JobHandle.ScheduleBatchedJobs is not necessary. Don’t try to update NativeContainer contentsDue to the lack of ref returns, it is not possible to directly change the content of a NativeContainer. For example, nativeArray[0]++; is the same as writing var temp = nativeArray[0]; temp++; which does not update the value in nativeArray. Instead, you must copy the data from the index into a local temporary copy, modify that copy, and save it back, like so:
Call JobHandle.Complete to regain ownershipTracing data ownership requires dependencies Use Schedule and Complete in the control thread您只能从同一个控制线程调用Schedule和Complete.如果一个作业依赖于另一个作业,请使用它JobHandle来管理依赖关系,而不是尝试在作业中安排作业。 Use Schedule and Complete at the right timeCall Schedule on a job as soon as you have the data it needs, and don’t call Complete on it until you need the results. It is good practice to schedule a job that you do not need to wait for when it is not competing with any other jobs that are running. For example, if you have a period between the end of one frame and the beginning of the next frame where no jobs are running, and a one frame latency is acceptable, you can schedule the job towards the end of a frame and use its results in the following frame. Alternatively, if your game is saturating that changeover period with other jobs, and there is a big under-utilized period somewhere else in the frame, it is more efficient to schedule your job there instead. Mark NativeContainer types as read-onlyRemember that jobs have read and write access to NativeContainer types by default. Use the [ReadOnly] attribute when appropriate to improve performance. Check for data dependenciesIn the Unity Profiler window, the marker “WaitForJobGroup” on the control thread indicates that Unity is waiting for a job on a worker thread to complete. This marker could mean that you have introduced a data dependency somewhere that you should resolve. Look for JobHandle.Complete to track down where you have data dependencies that are forcing the control thread to wait. Debugging jobsJobs have a Run function that you can use in place of Schedule to immediately execute the job on the control thread. You can use this for debugging purposes. Do not allocate managed memory in jobs在作业中分配托管内存非常慢,并且作业无法利用 Unity Burst 编译器来提高性能。Burst 是一种新的基于LLVM的后端编译器技术,可让您更轻松地完成工作。它采用 C# 作业并利用平台的特定功能生成高度优化的机器代码。 Further informationWatch the Unity GDC 2018: C# Job System playlist of talks. |
|
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 | -2024/11/23 10:37:59- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |