当前位置: 首页 > news >正文

西安网站建设推广专家安徽网站建设seo优化

西安网站建设推广专家,安徽网站建设seo优化,网站建设使页面内容居中,单一本地门户网站源码大家好,我是阿赵。接下来继续学习Unity的DOTS。首先来学习的是Jobs101项目,之前在Git上面检出的多个项目里面的其中一个:使用Unity6打开该项目,会看到这里主要有4个Step的场景:通过项目命名可以直观的知道,…

  大家好,我是阿赵。
接下来继续学习Unity的DOTS。
首先来学习的是Jobs101项目,之前在Git上面检出的多个项目里面的其中一个:
在这里插入图片描述

  使用Unity6打开该项目,会看到这里主要有4个Step的场景:
在这里插入图片描述

  通过项目命名可以直观的知道,这个Jobs101的范例项目,是打算通过4个步骤,一步步的向我们展示Job System的使用方法。

一、 Step1

  打开Step 1文件夹里面的Step1_NoJobs场景,并且运行:
在这里插入图片描述

  可以看到
在这里插入图片描述

  场景里面有很多红蓝色的方块,然后帧率非常低,非常的卡顿。

1、例子说明

  接下来需要对这个例子进行一定的说明,好让大家知道,Unity想通过这个例子向我们说明什么。因为接下来的步骤里面其实都是同一个例子,只是处理的方式不一样,性能也不一样而已。
在这个例子里面,有2种对象:

1. Seeker

  Seeker是探索者的意思,它在demo里面是蓝色的方块:
在这里插入图片描述

2. Target

  Target是Seeker需要寻找的目标,在demo里面是红色方块
在这里插入图片描述

  然后Seeker和Target上面挂载的Seeker和Target脚本,其实做的事情都是一样的,就是让自身沿着一个方向移动。

    public class Seeker : MonoBehaviour{public Vector3 Direction;public void Update(){transform.localPosition += Direction * Time.deltaTime;}}

  所以这两个脚本可以不用在意。

  这个Demo要做的事情是,先根据设定的数量,生成大量的Seeker探索者和Target目标,然后这些大量的Seeker和Target都是在按照一定的方向和速度在移动的。而Seeker既然成为探索者,那么它们是需要在不断的对自己附近进行探索,找到离自己最近的一个Target的。
切换到Scene视图,就可以看到:
在这里插入图片描述

  每一个蓝色的Seeker,都会通过一条线,找到离自己最近的一个Target。
然后在场景里面,我们可以找到一个Spawner的对象:
在这里插入图片描述

  它上面挂载的Spawner脚本,里面指定了Seeker和Target分别使用的预设对象,还有Seeker和Target分别生成的数量,比如截图里面就是各1000个。最后是生成的范围Bounds是500x500的范围内生成。
接下来的几个Step其实都是这个例子,都是Seeker寻找离自己最近的Target。

2、 Step1例子说明

  回头看看Step1这个例子,由场景的命名NoJobs,我们可以直观的理解到,这个例子,其实是没有使用JobSystem的。
那么使用传统的方法,如果我们需要找到每个Seeker最近的Target,一般有什么办法呢?很明显就是直接遍历然后对比距离的大小了。
所以在每个Seeker对象上面, 都挂载了一个FindNearest的脚本。
在这里插入图片描述

  这个脚本的作用很简单:

namespace Tutorials.Jobs.Step1
{public class FindNearest : MonoBehaviour{public void Update(){// Find nearest Target.// When comparing distances, it's cheaper to compare// the squares of the distances because doing so// avoids computing square roots.Vector3 nearestTargetPosition = default;float nearestDistSq = float.MaxValue;foreach (var targetTransform in Spawner.TargetTransforms){Vector3 offset = targetTransform.localPosition - transform.localPosition;float distSq = offset.sqrMagnitude;if (distSq < nearestDistSq){nearestDistSq = distSq;nearestTargetPosition = targetTransform.localPosition;}}Debug.DrawLine(transform.localPosition, nearestTargetPosition);}}
}

  就是先从Spawner里面拿到了所有的Target对象,然后逐个对比和自己的距离,找到最近的一个,然后画线。
这一步没什么问题,思路很正常。但由于对象的数量多,假设Seeker有m个,target有n个,那么它的计算次数就是m乘n了。所以,在场景里面各1000个对象的情况下,计算数量是1000x1000等于一百万次的距离计算和比较,就非常的卡顿了。

二、 Step2

  接下来打开Step 2文件夹里面的Step2_SingleThreadedJob场景:
在这里插入图片描述

  从Spawner可以看到,同样是分别创建1000个Seeker和Target,我们运行看看有什么区别:
在这里插入图片描述

  最大的区别在于,帧率飙升了,从刚才step1只有个位数的fps,达到了100fps以上。
是什么操作导致了这么大的提升呢?
先来看看Seeker使用的预设
在这里插入图片描述

  会发现之前的FindNearest脚本没有了,只剩下Seeker脚本。
而FindNearest的脚本变成在Spawner
在这里插入图片描述

  我们从场景的命名上看,SingleThreadedJob,我们大致可以猜到,这个Step2实际上是想告诉我们怎样用一个线程来运行Job System的。
所以我们可以打开Step2里面的FindNearest脚本看看:
在这里插入图片描述

  主要看这个部分,在设置了所有的Seeker和Target的Position之后,脚本创建了一个FindNearestJob,然后通过FindNearestJob又拿到了一个JobHandle。最后这个JobHandle调用了Complete方法。
这个脚本的注释里面已经说明了它所做的事情

1. 创建Job的实例并填充数据

//To schedule a job, we first need to create an instance and populate
its fields.

意思是,如果想计划一个job,我们首先需要创建一个实例并且填充它的字段。
所以

    FindNearestJob findJob = new FindNearestJob{TargetPositions = TargetPositions,SeekerPositions = SeekerPositions,NearestTargetPositions = NearestTargetPositions,};

2. 把Job实例放进job队列

//Schedule() puts the job instance on the job queue.

所以

JobHandle findHandle = findJob.Schedule();

意思是通过Schedule函数的调用,把job的实例放到job的队列里面。

3. 等待Job完成

        // The Complete method will not return until the job represented by// the handle finishes execution. Effectively, the main thread waits// here until the job is done.

  意思是,这个Complete函数会一直等待job的处理完成才会返回,在这个过程中,主线程是会一直在这里等待job完成。
所以当调用:

findHandle.Complete();

  这里其实主线程是会等待job执行的。
这个过程我们应该能理解了,它是在执行FindNearestJob。
在这里插入图片描述

  而打开这个Step2文件夹里面的FindNearestJob脚本看看
在这里插入图片描述

  这里实际上也是通过了两层循环去遍历所有的Seeker和Target,来寻找每个Seeker最近的Target。
但这里有些不一样,需要注意的地方:

1. 新数学库的使用

  之前挂载在Seeker上的FindNearest脚本,是使用了Vector3来代表坐标,然后使用Vector3。sqrMagnitude来计算向量平方长度的
这个例子里面,使用了Unity.Mathematics.float3来代替了Vector3,然后使用了Unity.Mathematics.math.distancesq来代替 Vector3.sqrMagnitude.
使用新的Unity.Mathematics.math数学库,是因为旧的数学库诸如Vector3、Quaternion等类型的内存布局非最优,GC压力大。而新的数学库在解决了这些问题之余,还可以利用Burst编译器优化,提升计算的性能。
为了这个目的,所以在生成Job实例之前,需要先通过遍历,把所有的Seeker和Target的坐标,转换成NativeArray数组

NativeArray<float3> TargetPositions;
NativeArray<float3> SeekerPositions;

  需要注意的是,Vector3是可以隐式转换成float3的,所以可以:

TargetPositions[i] = Spawner.TargetTransforms[i].localPosition;

2. 集中遍历

在之前的例子里面,都是单个Seeker对象在Update生命周期里面进行对Target列表的遍历。而这个例子里面,所有的遍历都集中在一起,通过一个线程去统一计算。
做了这么简单的修改之后,可以看到性能就有了突飞猛进的提高了。这让人更其他后面的两个步骤了。

三、 Step3

  接下来打开Step3文件夹下的Step3_ParallelJob场景
在这里插入图片描述

  运行看看:
在这里插入图片描述

  吓了一跳,怎么step2的帧率都有100fps,step3才40多?不要急,看看生成的数量:
在这里插入图片描述

  之前step1和step2都是Seeker和Target各生成1000个,这里变成了各5000个,从遍历的数量级来说,是整整25倍了,从100万次变成了2500万次了。如果改回各生成1000个,fps是有140多的。
在这里插入图片描述

  所以很明显,这次的Step3又在Step2的基础上有了较大的进步。从场景的名称看,ParallelJob,是平行工作的意思,因为Step2是一个线程执行Job,那么我们可以理解成Step3其实是多个线程同时执行Job了。
看看Step2和Step3的主要区别:

1、 Schedule方法的调用

  在Step3的FindNearest脚本里面,可以看到:

JobHandle findHandle = findJob.Schedule(SeekerPositions.Length, 100);

这里的说明是:

        // Execute will be called once for every element of the SeekerPositions array,// with every index from 0 up to (but not including) the length of the array.// The Execute calls will be split into batches of 100.

  意思是,Execute方法将会根据SeekerPositions数组长度的每个元素都调用一次,每次调用的index是从0一直到SeekerPositions 数组的长度(不包括数组长度本身)。
然后这些调用会分成每个批次调用100个。
怎么理解呢?
第一个参数很好理解,这个批处理里面我们总共需要处理多少个数据。而第二个参数,其实是指每个线程单次处理的时候,处理多少个数据,比如上面设置了100,那么第一个线程处理的将会是0-99号数据,第二个线程处理的是100-199号数据。
那么问题来了,如果这些数据之间是需要互相访问的,比如我在处理0-99号数据的时候,是需要拿到第110号的数据,怎么办呢?由于线程之间的是同时运行的,如果数据在线程中发生变化,其他线程是不知道的。如果真的需要把所有数据都在过程中修改并且可以互相调用,那么只能把调用批次设置成和数组总长度一样了。

2、 Execute方法的参数

  由于Schedule方法设置了总数量和批次,所以Job的Execute方法也会带有Index参数
在这里插入图片描述

  之前的Step2的Job,里面的Execute方法是没有Index参数的,意味着它每次进程会只调用一次。而Step3的Job,里面的Execute方法是带Index的,意味着根据数组的长度每一个元素会调用一次。
由于这样并行运行了,所以执行的效率就会比Step2更快了。
如果我们改成这样:

JobHandle findHandle = findJob.Schedule(SeekerPositions.Length, SeekerPositions.Length);

  那么其实每次都是一个批次处理完所有元素,那么实际的执行效率将会和Step2一样了。

四、 Step4

  打开Step4文件夹的Step4_ParallelJob_Sorting场景:
在这里插入图片描述

  运行之后
在这里插入图片描述

  看到帧率只有不到30,不过还是不用急,看看生成的数量。这次生成的是Seeker和Target各一万个,所以实际的遍历数量是1亿次了。
如果是这个数量级,在Step3里面运行:
在这里插入图片描述

  帧率就降到只有10几了。
从场景命名看,ParallelJob_Sorting,比Step3多了一个Sorting,意味着多了一个排序,那么具体是做了什么呢?
主要的区别是:

SortJob<float3, AxisXComparer> sortJob = TargetPositions.SortJob(new AxisXComparer { });FindNearestJob findJob = new FindNearestJob
{TargetPositions = TargetPositions,SeekerPositions = SeekerPositions,NearestTargetPositions = NearestTargetPositions,
};JobHandle sortHandle = sortJob.Schedule();
JobHandle findHandle = findJob.Schedule(SeekerPositions.Length, 100, sortHandle);

在这里创建了一个sortJob,这个是一个排序的Job,具体的方法是AxisXComparer :

    public struct AxisXComparer : IComparer<float3>{public int Compare(float3 a, float3 b){return a.x.CompareTo(b.x);}}

  这个排序是对TargetPositions里面的所有元素的x轴进行从小到大的排序的。
然后在FindNearestJob里面,再次利用AxisXComparer 方法,找到离当前Index的Seeker的x方向距离最短的Target,作为一个备选答案,求出当前Seeker和这个Target的实际距离的平方,然后再从这个Target的序号分别往前和往后遍历,每个Target和当前的备选Target做对比,看有没有比备选答案距离更短的。
我个人觉得,这只是一个优化方式的举例,各位不用太较真这个排序算法本身。这里是做了一个小算法,由于已经知道了目标和当前Seeker和备选Target的实际距离的平方,而实际距离的平方是x偏移x偏移+y偏移y偏移,所以只要计算下一个Target的x偏移*x偏移大于当前的距离平方,就可以不用考虑这个Target了:
在这里插入图片描述

  而由于之前已经通过排序方法对TargetPositions进行排序了,所以应该来说从x最近的Target开始,从往左或者往右的其中一边,应该有连续的一大段都不需要参加计算。
不要在意这个算法本身,需要留意的是,我们可以在执行Schedule函数的时候,指定多一个handle,用于多线程处理。不过由于这个例子本身的计算还是比较简单,所以感受不出很大的变化。

五、 总结

  看完了4个例子,已经基本掌握了Unity的Job System的使用了,这个官方例子告诉了我们这些内容:

1、 Job System是可以单独使用的

  在这几个例子里面,并没有使用到ECS,只是普通的常用代码方式编写。所以并不是说它是DOTS中的一员,就一定要和ECS一起使用的,也可以单独使用。

2、需要使用Job System,需要的步骤是:

1.创建Job实例

  通过new方法创建出想要的Job实例,并且填充里面的字段数据。创建出来的实例并不会立刻执行

2.调用计划

  改过Job实例调用Schedule方法,可以传入总长度和批次,也可以不传。

3、等待完成

Schedule返回的handle调用Complete方法,等待Job完成

http://www.dtcms.com/a/615028.html

相关文章:

  • 网站建设合同要求绍兴 网站建设
  • 字节技术总监笔记:linux多线程>>进程线程互斥管道
  • 个人网站备案 内容黑龙江建设兵团知青网站
  • AI 大模型如何给 CAD 3D 模型“建立语义”?
  • MYSQL指令合集
  • 北京企业网站百度wordpress结构化数据插件 sign 检测失败
  • 网站建设需要注意哪些事项前端网站开发课程
  • 周期购那个网站做的比较好太原seo整站优化
  • 加强网站安全建设说明报告范文湖南长沙招聘
  • 精品成品网站源码关键词优化易下拉效率
  • 做网站前期框架图怎么做解析视频网站
  • 终端能力等级Category介绍
  • 计算机网站建设论文总结农村自建房设计图 户型图
  • 做爰网站下载地址品牌如何推广
  • 云南文山网站建设制作WordPress不使用MySQL数据库
  • 全椒县城乡建设局网站纯注册app拉新平台
  • 合肥家居网站建设怎么样广告联盟平台系统
  • PID调参实战:Ziegler–Nichols(Z-N)搜索方案全解析
  • AES加密,前端加密,后端解密
  • SK Keyfoundry增加Sic产能
  • 温州建站平台wordpress 七牛云
  • Spring Boot与MyBatis
  • 大区直播间网站开发制作研究网站开发意义
  • 西安网站建设价格明细北京西城注册公司
  • 提升知识索引性能的技术手段
  • 【数据结构】【xjtuse】八股文单元小测
  • 无锡网站建设开发网站建设终身不用维护
  • 苏州网站建设排名安卓开发课程
  • 前端工程化未来,模块联邦与微前端
  • 广州网站建站公司如何做社群营销模式