Unity云原生分布式运行时

  //  

元宇宙时代的来临对实时3D引擎提出了诸多要求,Unity作为游戏行业应用最广泛的3D实时内容创作引擎,为应对这些新挑战,提出了Unity云原生分布式运行时的解决方案。LiveVideoStack 2023上海站邀请到Unity中国的解决方案工程师舒润萱,和大家分享该方案的实践案例、面临的问题、解决方式,并介绍了Unity目前对其他方案的构想。

文/舒润萱

整理/LiveVideoStack

大家好,我叫舒润萱,现在在Unity中国担任解决方案工程师,主要负责开发的项目是Unity云原生分布式运行时。

ed6ebda86bfa36c1a7fe0abf557d7c19.png

首先介绍一下Unity。Unity是游戏行业应用最广泛的3D实时内容创作引擎。截止2021年第4季度,70%以上移动平台的游戏是使用Unity开发的。

e09413f240e95c07784246a4fdab667e.png

但Unity不止是一个游戏引擎,Unity的业务目前涉及到汽车行业、建筑行业、航空航天行业、能源行业等等各行各业。

99cbf78467603292fb25fd07f5b7ab9d.png

Unity的业务在全球都有开展,在18个国家有54个办公室。在中国,在上海、北京、广州都有办公室,在临港也开了一间新的办公室。

cb58be498d2f83760e7bd6382b04922a.png

Unity覆盖的平台是最广泛的,它支持超过20个主流平台,率先支持了Apple新发布的vision OS。

dbe3ee5bf6fef7b14f8b37fc41cdf014.png

我今天的分享将从这六个方面进行。

-01-

元宇宙时代的挑战

首先,实时3D引擎在元宇宙时代会遇到什么挑战?

8952652407ec455e4ebbbdc73c702515.png

Unity认为元宇宙会是下一代的互联网,它将是实时的3D的、交互的、社交性的,并且是持久的。

70783c171c383964ee98776c0929ba66.jpeg

元宇宙会是一个规模庞大的虚拟世界,这个虚拟世界里有很多参与者,它将会是一个大量用户实时交互的场景。同时,元宇宙它必须是一个持续稳定的虚拟世界。

参与者在这个虚拟世界里对它进行的改变将会随着时间保留下来,并且它是稳定的状态。这就对实时3D交互造成了很多挑战。

4d25b0ae19bd13c8bf87a0f24be2d485.png

首先,因为这个虚拟世界将会是一个大规模的高清世界,里面将会有数量庞大的动态元素、静态元素,所以它对实时3D引擎提出了渲染的挑战。

其次,它伴随着大量的网络传输,对引擎的可扩展性和可伸缩性提出了很高要求,所以对运行平台来说也是一个不小的挑战。

最后,因为这个虚拟世界会有超大规模的物理仿真,用户将会在其中进行大量的实时交互,所以这对运算资源也是一个巨大的挑战。

-02-

Unity分布式运行时

为了应对这些挑战,Unity提出了分布式运行时的解决方案。

e6959ee2ddf1069fbd0b62c5c32f07a4.png

这个方案由两部分组成,第一个部分是Unity云原生的分布式渲染,第二个部分是Unity云原生分布式计算。

ffb65902d26b1edb0a14cf1dc7ae992b.png

首先介绍分布式渲染。要做一个多人联网的体验,可以从最简单的Server-Client架构入手。如图所示,在这个架构中有一个中心化的Server,它可以服务于多个Client。这张图上画了两个Client,这是最简单的架构。

878dd52c32abbf306f3eb7e960401d8b.png

为了对应大规模的渲染压力,Unity把用户的Client端拆成了Merger和Renderer。在这里面,一个Merger对应多个Renderer,这些Renderer将会负责虚拟世界的渲染。我们把一帧的渲染任务拆分成很多个子任务,分别交给这些Render进行。Merger负责把Renderer渲染的画面组合成最终画面,之后通过WebRTC推流的方案推给用户的客户端。要注意,这里除了用户的客户端以外,所有的环境都是运行在云上的。

73dc6c695712460d942bad58fad86e22.png

图示为一个最简单的屏幕空间拆分,即把一个完整的画面在屏幕空间上拆成几个部分。除此之外还有其他拆分方式,例如通过时间拆分:假设有4个Renderer,第一个Renderer渲染第一帧,第二个Renderer渲染第二帧,第三个Renderer渲染第三帧……以此类推,再把它们组合成一个完整的序列。

Unity现在也在实验一个新的拆分方式:在Merger上渲染一个比较高清的近景,再把远景拆分到几个Renderer上。Renderer的几个画面可以组成一个天空盒推到Merger上,这样Merger就可以同时拥有细节比较丰富的近景和远景。Unity的架构允许开发者根据自己的业务需求定义自己画面的拆分方式。

52f20f4815ed0c68824932a2c9c594c3.png

回到这个架构图,想象在一个大型的虚拟世界里,有成千上万用户连入运行时,通过分布式渲染,把原本一个进程服务对应一个用户的场景,拆分成好几个进程服务于一个用户,因此这个场景就会对Server提出巨大的挑战。为了应对这个挑战,Unity提出了分布式计算。

7c6355c6c41d4efa05319972f0240164.png

这种分布式计算把Server拆分成了Server和Remote,用 Remote分担Server的一些运算压力。

757bf17264093889d2b22268dbc1f741.png

简要概述一下Unity是怎样把计算任务分配给Remote的。Unity游戏的业务流程可以简单拆分成数据和数据处理器,数据叫做Component,数据处理器实际上就是业务逻辑,代码叫System。每个System有自己感兴趣的数据,它会读取这个感兴趣的数据,通过逻辑进行数据更新。

abb695300ce7879a3c2a700d4c5fbced.png

619464b8203f49e43e25682d03913973.png

Unity把这个System跑在远程的机器上,而不是本地,之后把它感兴趣的数据通过网络发送给远程的机器,它就可以像本地处理一样处理这个数据,再把更新的数据通过网络发回给Server。

-03-

实践案例展示

下面展示三个案例。

92d2693821d9d56b3f4eb905e8853e7c.png

00ff96ffdcdaea5eda0f1b8d4fd4ac1c.png

第一个是Unity内部用来测试分布式计算性能的demo。在这个场景里有4个塔,每个塔都由几万个方块组成,总共有89000多个方块。

上面的视频展示的是它在单机情况下的物理仿真情况,下面的这个视频是开启4个Remote节点进行分布式计算的情况。可以看到,上面的仿真非常慢,下面的仿真偏正常。经过测试,Unity的仿真速度可以从每秒10次仿真提升到每秒37次仿真。

a8ad178061d2936df040eaff68829e90.png

第二个demo是Unity中国和中国移动咪咕合作的项目,叫“青霄”,是一个直播互动剧。这个项目是一个非常高清的场景,它的整个场景大约有三亿个三角面。Unity使用了三个Renderer的分布式渲染。在不同场景下帧率的情况不太一样,基本能够提升1.5倍~2.8倍。这个场景非常仿真、非常真实,里面的物体的面数都非常高。

e79407ac7f974baa1fd60a7d3a602fe3.png

最后一个demo是Unity中国和中国移动咪咕合作的叫“星际广场世界杯”的项目,这个项目是在2022年世界杯期间上线的,它主打的是万人同屏的元宇宙体验。画面上展示的是一个5000人的场景,上下两个场景是一样的,场景里面共有5000个用户参与,也是4k的环境。

这个项目使用了分布式计算+分布式渲染,因为有这么多用户的参与,没有分布式计算就无法跑起来。这个项目的分布式物理是由MOTPHYS公司提供的,用了3个Renderer的分布式渲染,可以把整个帧率从大约15帧左右提升到30帧左右。

-04-

视频编解码与实时渲染

下面进入今天的正题,视频编解码与实时渲染。

3a4eec05e7ca60584dd35664658c4941.png

为什么要在分布式渲染中引入视频编解码?

可以看这个简化的分布式渲染架构图。Merger和Renderer之间的通信是通过网络TCP传输的,即图像是通过TCP传输的。

c9ac5dea80769db9f38c4a19d0be1bce.png

这个做法有两个问题:

一是会遇到网络带宽瓶颈,在实时3D交互的场景下,一般至少要Target到1080p60帧。而如果传的是8 bits RGBA的图像格式,需要的带宽是4Gbps,远远超出了常见的千兆带宽1 Gbps,很多机房没有办法接受。即使对Raw Data进行YUV420的简单压缩,它也要占用约1.6 Gbps的带宽,超过了千兆。

其次是性能问题。因为Unity的画面是从GPU渲染出来的,为了通过网络传输这个画面,要先把GPU显存上的图像数据先回传到CPU端。这个回读就会导致需要暂停渲染的流水线,因为目前Unity优化较好的实时3D引擎的渲染都是流水线形式的,即CPU产生场景数据,再把这个场景数据交给GPU渲染,同时CPU可以进行下一帧的计算。但是如果数据是反过来从GPU回到CPU,意味着需要把流水线暂停,包括GPU、CPU上的所有任务都要等待回读完成之后才能继续工作。

这里截取了一张Unity的回读指令,它消耗的时间是7.22毫秒,在实时交互的场景下,希望做到的是60帧的体验,即16毫秒,因此用7.22毫秒的时间把这张图片读回来是无法接受的。

cd3e545e07edd7c0aa43ffe9f9ed56bc.png

为了解决这两个问题,Unity为分布式渲染的方案开发了Unity Native Render Plugin。Unity的目标运行环境是云端,Linux和Vulkan的图形API。云端一般配备的是NVIDIA GPU,希望采用NVIDIA GPU进行硬件的视频编解码。在这个方案下面,Unity还是负责虚拟世界的渲染和逻辑。FFmpeg负责视频的编解码以及把Unity的图像在显存内部拷贝给NVIDIA的Video Codec SDK,Unity的插件就是连接这两个部分以实现目标。

ad31937a840423b799cd3a8e072ef011.png

这几个流程图展示了Unity一定要硬件编解码的原因。橙色的框为显存上的数据,蓝色的框为CPU端内存上的数据。

最左边的流程图是最简单的。如果要传输一个Raw Data的方案,在GPU上把图像渲染完成之后,要通过回读把它读到CPU端,这是非常耗时的操作。在读完这个图像之后,把它通过TCP发送给Merger,占用带宽非常大,这是不可接受的。

如果引入的是一个软件编解码的场景,需要图像的数据在CPU端存在。所以回读还是无法避免,操作仍然十分耗时。在软件编解码时进行的编码和解码操作,同样非常耗时。虽然这对视频来说没问题,但是对于60帧的实时场景是不太能接受的。但是这个方案的带宽其实优化了很多,因为视频码率至少比4 Gbps、1.5 Gbps好很多。这是软件面板的情况,它最多只是优化了带宽。

最后是硬件编解码。硬件编解码是在渲染完成之后,通过显存间的拷贝,把渲染的结果拷贝到Media的CUDA端,然后做硬件编码和硬件解码。虽然是一次拷贝,但是因为是在一张显卡的显存上,所以拷贝过程非常快。硬件编码完成之后,视频帧数据会自动回到CPU端。虽然其本质上也是一次回读,但是它和流水线无关,相当于是在另一条线上完成的,所以这个回读消耗的只有从PCIE到CPU到内存的带宽的速度,回读的不是一个完整的图像,而是压缩好的视频帧,它的数据量也大大减小,所以回读非常快。之后通过TCP发送视频帧,带宽非常小,在Merger端解码上传的Buffer也比原来小,解码也很快。最后把解码完成后的Image,Copy 到Vulkan Texture,这也是在同一块GPU内显存里的操作。

可以看到,硬件编解码的流程优化了很多,每一步都能得到很高的提升。

dd86b859ec2b11e5a65e70a5289106d6.png

介绍一下Unity是怎样配置FFmpeg的CodecContext的。Unity使用了两个CodecContext,Video Context是真正用来做视频编解码的Context,它是一个TYPE CUDA的Hwdevice。第二个CodecContext用的是Vulkan的Context,这个Context的作用是

和Unity的Vulkan Context进行交互,所以在初始化时不会使用到它默认的API,而是从Unity的Vulkan Instance里面取出了所有必要的信息,把它交给FFmpeg的Vulkan Context,最后通过这种方式,就可以让FFmpeg的环境和Unity的环境存在于同一个Vulkan的运行环境中。

0515f8a5bd7532e21149193cdfcd4973.png

Video Context做初始化时,没有用到它默认的Hwdevice Context create的API,用到的是create derived的API。这个API需要传入另一个Hwdevice Context,它能保证做初始化时和另一个Hwdevice Context使用的是同一个物理GPU,这样才能真正地做显存间拷贝。这里选取的边界码的格式是H.264,8 bit,NV 12。

为什么要选用这个格式呢?其实是受硬件限制,因为目前NVIDIA硬件不支持10 bit的H.264的解码。H.265则耗时较大,不太满足60帧的需求,所以不得不选取这个格式。同时Unity是Zero Latency的配置,GOP的Size是0,保证视频每一帧都是Intra Frame。这有两个好处,一是它的延迟非常低。其次,因为分布式渲染在管理的时候可能会随机丢弃帧,如果有预测帧则不好丢弃,在全是I帧的情况下可以随机丢弃。

-05-

画质与性能优化

接下介绍Unity在画质方面和性能方面对插件进行的一些优化。

bd0ef1bdc7fd84f143cc145d74984551.png

首先是Tone-Mapping。Unity之所以这样做是因为遇到了Color-Banding问题,这个问题由两个因素导致:

首先,传输的图像不是一个普通的SDR图像,而是HDR图像;

其次,选取的格式只有8 bit,它导致了颜色精度和范围都是受限的。

954ec66afdb8d766ad3cc6fb83bee5d4.png

为了理解为什么Unity传输的是HDR图像,我给大家简单介绍一下渲染里面的一帧是怎么生成的。图示为简化的渲染过程,真正的渲染过程要比这复杂很多。

渲染过程可以简单分为三部分:

第一个部分叫Pre-Pass,它的作用是生成后续渲染阶段所需要的Buffer,在这一阶段里会预先生成Depth,即预先生成深度缓冲。深度缓冲预先生成最主要的好处就是可以减少Over-Draw,很多被遮挡的物体直接被剔除掉,后续步骤中就可以不用画。

第二步是渲染中最主要的一个步骤,我把它叫做Lighting Pass,主要的作用是计算光照,生成物体的最终颜色。这种Pass的实现方式有很多种,例如前向渲染、延迟渲染等。无论是使用哪种实现方式,它的最终目的都是生成一张Color Buffer,即颜色数据。

最后一个步骤一般成为后处理Post-Processing。这个步骤的主要作用是对输出的Color Buffer进行图像处理。经过Post-Processing产生的图像,颜色比单纯的Color Buffer自然很多。Post-Processing里面的效果大部分都是屏幕空间效果,例如要做一个辉光的效果,画面中有一盏灯非常亮,在它的边缘会有一些柔和的光效溢出,这个效果基本上就是通过Post-Processing实现的。

c4317bc78085bc963990750ee385ad90.png

屏幕空间效果会有什么问题?在分布式渲染中,渲染任务是被拆分开的,所以真正做渲染的Render的机器,实际上是没有全屏信息的,它没有办法做后处理。

对此Unity的解决方案是将后处理交由Merger进行。Renderer渲染到Color Buffer生成之后,就把Color Buffer传给Merger,Merger先把这个Color Buffer合起来,再总体做一次后处理。

那么,为什么这一过程中传的Colour Buffer是HDR的?因为在渲染中,光照模型一般都是物理真实的,所以为了之后做后处理,Colour Buffer本身是HDR的。

如图所示,这是把Black Point和White Point分成设置成0和1的效果,但是实际上这张图最高的值可以到90以上。如果是室外的场景,最高值可以到一万多,所以这张图的动态范围是非常高的。

16eae2aa857d46c04dc21ada5b67de1f.png

同时,因为传的是动态很高的HDR图像,加上使用的是8bit的编码,所以必须找到一个方法把HDR的Corlor Buffer映射为SDR的Buffer。对于这一映射也有一些要求,其一,它需要可逆;其二,它需要保留更大的表示范围,并且尽量减少精度损失。

Unity在这方面采用的是AMD提出的Fast Reversible Tone-Mapper,它有许多好处:

首先它是Unity SRP原生支持的,是Unity的可编程的渲染管线;其次它是可逆的,如图是它的公式;再次,它保留的数字范围更大,精度损失更小。如图上这一图像,y=x可以理解为原始颜色,经过Tone-Mapper处理后,它的取值范围永远在0~1之间,同时当它的值越小时,斜率越大,代表它能表示的数字越多,可保留的精度越高。有时在渲染中会遇到颜色值非常高的情况,但这种情况少之又少,更多的还是保留在0~1的范围区间内。因此Tone-Mapper能够帮助我们在0~1这个区间范围内保留更多的精度;最后它非常快,在AMD的GCN架构的显卡下,MAX RGB会被编译为一个指令,整个运算中只有3个指令,Max、加法和除法,所以它是非常快的。

60219e3c70cb1e651b89c838f67da6e7.png

如图,可以对比使用Tone-Mapping前后的效果。最左边是未经任何处理的原始图像;中间是不使用任何Tone-Mapping,经过8 bit的编解码、网络传输,再解码回来的情况;最右边是应用了Fast Reversible Tone-Mapping的情况。可以看到里面的背景有很多细节纹理,代表其中高频的信息比较多。在高频信息较多的场景下,应用了Fast Reversible Tone-Mapping之后的效果和原始图像的效果对比,已经看不出什么区别了。

86101ee24c257d0f3d4e446d71f810b3.png

但是如果场景里低频的信息较多,例如渐变较多,即使运用了Tone-Mapping,也没有办法完全解决这个问题。可以看到,原始图像上的渐变非常柔和,在无Tone-Mapping的情况下,色带肉眼可见。但是即使引入了Fast Reversible Tone-Mapping,也只能减缓色带问题,比起原始图像而言还是差了很多,目前没有更好的办法解决这一问题。

ce2d25f8b6e7b60e468175d160888f38.png

Unity对插件进行性能优化的另一个方式是Vulkan同步。因为涉及GPU内部的显存拷贝,而且它是Vulkan拷贝到CUDA的操作,所以它是一个GPU-GPU的异步操作。异步操作要求开发人员对于操作做好同步,即在编码时,要保证Unity渲染完成之后才进行编码,解码时要保证解码完成之后,才能把这张图像Copy给Unity,让Unity把它显示在屏幕上。

dafa30cd3e935c26fd562035e5777ab2.png

Unity Vulkan Native Plugin Interface提供了一种同步方式。这里主要关注框出的两个Flag:

上面的Flush Command Buffer,指在Unity调用自定义渲染事件时,Unity会先把它已经录制好的渲染指令提交到GPU上,这时GPU就可以开始执行这些指令了。

下面的Sync Worker Threads,指在Unity调用自己定义的Plugin Render Event时,会等待GPU上所有的工作全部完成之后才会调用。

d139a95f426d61101cad9ed0cfe04747.png

这两个方式组合起来确实能满足需求,确实可以做到同步,但是这种方式也打断了渲染流水线。使用这种同步方式,在调用Plugin Event之前,所有程序要全部停下,等待GPU完成操作。因此这个方式虽然能实现同步,但非常耗时。

如图展示的就是在这个同步方式下的情况。在简单的场景下,它耗时5.6毫秒。所以这种方式虽然能同步,但是性能非常差。

19587bd330182521871e740e1dfd2025.png

Unity只提供了上述的同步方式,因此我们只能转向Vulkan自带的同步原语。Timeline Semaphore是Vulkan在1.2版本的SDK里提出的新型的Semaphore,它非常灵活,而且是FFmpeg原生支持的。这里是FFmpeg的Vulkan Context的Frame,它通过Timeline Semaphore同步。在FFmpeg里,它主要被用于GPU-CPU同步,但它也可以用于GPU-GPU同步。

Unity提供的同步方式只有上述两个Flag,它无法直接使用底层的同步原语,但是它允许我们Hook Vulkan的任何一个API,即在 Hook之后,Unity在调用Vulkan的API时,它其实调用的是我们自己定义的Hook的版本,因此我们使用了Vulkan Hook介入Unity的渲染,在Unity把一帧的渲染提交给GPU之前,通过Hook提交的API,把FFmpeg的Semaphore 塞到提交里面,就可以保证在渲染完成之后会通知Timeline Semaphore,FFmpeg会等Semaphore被通知之后再执行。通过这种方式,这个同步也能达成目的,而且不会打断渲染流水线。

aa328e8b62a1d7fd5ae24c6b2628fc90.png

如图所示,上面为5.6毫秒耗时的情况,下面则完全把这一耗时消除了。因为在这个情况下,不需要在调用Render Event之前就提交渲染指令,而是在GPU上通过Timeline Semaphore和FFmpeg的Command进行同步,因此把这一部分完全省去了。在这个简单场景中大概有5.6毫秒左右的提升,经过测试,在复杂场景中则会有10~20毫秒不等的提升。

c40de8f2a8939145e97929ce90a15d18.png

另一个性能优化方案是多重缓冲,这是渲染中非常常用的技巧。在渲染中,常常会用多重缓冲来减少画面的卡顿、撕裂等情况,三重缓冲还能够提高帧率的稳定性、提高渲染性能。

多重缓冲的引入会把渲染变成Single Producer, Single Consumer的流水线模型,即渲染流水线。正是因为有多重缓冲,才能形成流水线,让GPU往一个缓冲中写入时,CPU可以开始准备下一个缓冲,GPU可以同时往另一缓冲区写入下一帧数据。

在编解码的插件中,Unity也引入了多重缓冲来提高性能,使用硬件编解码代替图像上屏操作。渲染的图像没有显示在屏幕上,而是通过网络发走,这是通过多重缓冲的方式实现的,和渲染有同样的效果。在引入多重缓冲后,Unity的渲染和编解码器会分别作为Producer和Consumer进行渲染和编解码的流水线。

-06-

总结与未来展望

7e3751e0453a17d3760c67136aa66e83.png

在分布式渲染的解决方案中会遇到网络带宽和性能问题。

首先,通过引入视频编解码可以解决了网络带宽的问题,采取硬件编解码避免GPU-CPU的回读,避免打断渲染的流水线。为了实现这个目标,Unity开发了Unity Native Rendering Plugin来对接Unity和FFmpeg底层的Vulkan和NVIDIA Codec 的SDK。

因为选取的编解码格式,方案中还遇到了色带问题,因此在方案中我们引入Tone-Mapping优化画质,通过FFmpeg自带的Timeline Semaphore,把Unity的渲染指令和FFmpeg的拷贝指令和编解码指令进行同步,保证编解码结果正确。

最后,Unity通过引入多重缓冲提升性能,减少帧率不稳的情况。

bf8bb3da4bf1360841598d790e533e3a.png

目前Unity还在探索一些其他的方案。

首先,Unity希望尝试Vulkan自己推出的Vulkan Vider Extensions。它在2023年1月左右才真正进入Vulkan的SDK,成为一个正式的功能。这个Extension非常新,所以到目前为止Unity还没有机会进行尝试,但一直在关注。如果使用这一Extension,就可以完全避免前文所述得到显存间拷贝、同步等问题。因为这一应用程序没有引入别的GPU端的运行环境,完全在Vulkan内部运行,因此我们不需要拷贝,直接使用Unity的结果即可。

其次,Unity在对接其他GPU的硬件厂商,尝试其他硬件编码。Unity目前正在和一些国产的GPU厂商对接,他们表示他们的硬件编解码能力会有所提升,支持的格式不再限制于8 bits了。

最后,Unity还希望尝试一些要使用GPUDirect RDMA、CXL共享内存等特殊硬件的方案。GPUDirect RDMA允许直接把GPU显存里的东西直接通过网络发走,能够减少回读。CXL共享内存,顾名思义是个共享内存,相当于很多台机器共享一个远程的内存池,因此它的带宽和延迟都是内存级别。这一方案至少允许我们在分布式渲染的环境下不进行视频编解码,可以使用Raw Data的方式,把Raw Data存在远程内存中。

以上就是今天的分享内容,谢谢。


db6a2cd72f41a3beb45c35531faf0b07.jpeg

扫描图中二维码或点击“阅读原文 

直通LiveVideoStackCon 2023深圳站 9折购票通道

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/83650.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

倒计时列表实现(小程序端Vue)

//rich-text主要用来将展示html格式的&#xff0c;可以直接使用这个标签 <view class"ptBox" v-for"(item,index) in orderList" :key"index"> <rich-text :nodes"item.limit_time|limitTimeFilter"></rich-text>…

2023_Spark_实验十二:Spark高级算子使用

掌握Spark高级算子在代码中的使用 相同点分析 三个函数的共同点&#xff0c;都是Transformation算子。惰性的算子。 不同点分析 map函数是一条数据一条数据的处理&#xff0c;也就是&#xff0c;map的输入参数中要包含一条数据以及其他你需要传的参数。 mapPartitions函数是一个…

网络编程day03(UDP中的connect函数、tftp)

今日任务&#xff1a;tftp的文件上传下载&#xff08;服务端已经准备好&#xff09; 服务端&#xff08;已上传&#xff09; 客户端&#xff1a; 代码&#xff1a; #include <stdio.h> #include <string.h> #include <stdlib.h> #include <sys/types.h…

编译工具:CMake(八) | cmake 常用指令

编译工具&#xff1a;CMake&#xff08;八&#xff09; | cmake 常用指令 基本指令 基本指令 ADD_DEFINITIONS向 C/C编译器添加-D 定义&#xff0c;比如:ADD_DEFINITIONS(-DENABLE_DEBUG-DABC)&#xff0c;参数之间用空格分割。 如果你的代码中定义了#ifdef ENABLE_DEBUG #end…

Java 调用 GitLabAPI 获取仓库里的文件件 提交记录

1. 需求 项目组 需要做统计&#xff0c;获取每个开发人员的代码提交次数&#xff0c;提交时间&#xff0c;提交人等等&#xff0c;因代码在GitLab上管理&#xff0c;所以需要调用GitLabAPI来获取。 2. 开发 API官网&#xff1a;https://docs.gitlab.com/ee/api/ 2.1 创建自…

java Spring Boot验证码美化,白色背景 随机四个数 每个字随机颜色

我前文 Spring Boot2.7生成用于登录的图片验证码讲述了生成验证码的方法 但是这样生成验证码 非常难看 比较说 验证码是要展示到web程序中的 这样让用户看着 属实不太好 我们可以将接口改成 GetMapping(value "/captcha", produces MediaType.IMAGE_PNG_VALUE) …

RocketMQ 发送事务消息

文章目录 事务的相关理论事务ACID特性CAP 理论BASE 理论 事务消息应用场景MQ 事务消息处理处理逻辑 RocketMQ 事务消息处理流程官网事务消息流程图 rocketmq-client-java 示例&#xff08;gRPC 协议&#xff09;创建事务主题生产者消费者 rocketmq-client 示例&#xff08;Remo…

代码随想录Day1 数组基础

本文详细说明和思路来源于: 代码随想录 视频讲解: 手把手带你撕出正确的二分法 | 二分查找法 | 二分搜索法 | LeetCode&#xff1a;704. 二分查找_哔哩哔哩_bilibili Leetcode T 704 题目链接 704. 二分查找 - 力扣&#xff08;LeetCode&#xff09; 题目概述1: 思路: 1.因…

基于微信小程序的高校宿舍信息管理系统设计与实现(源码+lw+部署文档+讲解等)

前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战✌&#x1f497; &#x1f447;&#x1f3fb;…

局域网下共享文件夹全流程

请注意&#xff1a;配置共享文件夹以便他人无需输入账户和密码访问可能带来安全风险。请确保你明白这一点并在适当的网络环境中操作。 以下说明是基于 Windows 系统的&#xff1a; 步骤 1&#xff1a;共享文件夹 找到你想要共享的文件夹&#xff0c;右击选择“属性”。 转到…

Docker从认识到实践再到底层原理(六-1)|Docker容器基本介绍+命令详解

前言 那么这里博主先安利一些干货满满的专栏了&#xff01; 首先是博主的高质量博客的汇总&#xff0c;这个专栏里面的博客&#xff0c;都是博主最最用心写的一部分&#xff0c;干货满满&#xff0c;希望对大家有帮助。 高质量博客汇总 然后就是博主最近最花时间的一个专栏…

RabbitMQ学习总结(11)—— RabbitMQ 核心概念与架构

MQ 技术概述 什么是 MQ MQ(message queue),从字面意思上看,本质是个队列,FIFO 先入先出,只不过队列中存放的内容是 message 而已,还是一种跨进程的通信机制,用于上下游传递消息。在互联网架构中,MQ 是一种非常常见的上下游 “逻辑解耦+物理解耦” 的消息通信服务。使用…

网页的快捷方式打开自动全屏--Chrome、Firefox 浏览器相关设置

Firefox 的全屏方式与 Chrome 不同&#xff0c;Chrome 自带全屏模式以及APP模式&#xff0c;通过简单的参数即可设置&#xff0c;而Firefox暂时么有这个功能&#xff0c;Firefox 的全屏功能可以通过全屏插件实现。 全屏模式下&#xff0c;按 F11 不会退出全屏&#xff0c;鼠标…

grafana对指标进行组合计算

在使用Grafana配置图表看板时&#xff0c;我们可能需要对多个查询条件筛选出来的结果进行计算&#xff0c;把计算结果生成最终的图表。此时需要用到transform功能【add field from calculation】&#xff1a;

flutter iOS 缺少通知权限,缺少位置权限

App Store Connect 亲爱的开发者, 我们发现了一个或多个问题与您的应用程序&#xff0c;“hayya附近的朋友Chat&Meet”1.0.3(1)最近的交付。您的交付是成功的&#xff0c;但您可能希望在您的下一次交付纠正以下问题: ITMS-90078:缺少推送通知授权-你的应用程序似乎注册了…

pytorch的卷积层池化层和非线性变化 和机器学习线性回归

卷积层&#xff1a; 两个输出的情况 就会有两个通道 可以改变通道数的 最简单的神经网络结构&#xff1a; nn.Mudule就是继承父类 super执行的是 先执行父类函数里面的 forward执行的就是前向网络&#xff0c;就是往前推进的&#xff0c;当然也有反向转播&#xff0c;那就是…

福建福州大型钢结构件3D扫描全尺寸三维测量平面度平行度检测-CASAIM中科广电

高精度三维扫描技术已经在大型工件制造领域发挥着重要作用&#xff0c;特别是在质量检测环节&#xff0c;高效、高精度&#xff0c;可以轻松实现全尺寸三维测量。本期&#xff0c;我们要分享的应用是在大型钢结构件的关键部位尺寸及形位公差检测。 钢结构件&#xff0c;是将多…

fabic.js Quadratic Curve /可控制的曲线

需要绘制一条可控制的贝塞尔曲线&#xff0c;发现fabic官网中一个demo有点类似。感兴趣的可以移步官网查看demo。 官网的demo是对于html 而言的&#xff0c;放在vue中需要变换一下&#xff0c;具体代码如下&#xff1a; <template><div class"dashboard-contai…

个人所思所想录

&#x1f9d1;‍&#x1f4bb;作者名称&#xff1a;DaenCode &#x1f3a4;作者简介&#xff1a;CSDN实力新星&#xff0c;后端开发两年经验&#xff0c;曾担任甲方技术代表&#xff0c;业余独自创办智源恩创网络科技工作室。会点点Java相关技术栈、帆软报表、低代码平台快速开…

Java版分布式微服务云开发架构 Spring Cloud+Spring Boot+Mybatis 电子招标采购系统功能清单

项目说明 随着公司的快速发展&#xff0c;企业人员和经营规模不断壮大&#xff0c;公司对内部招采管理的提升提出了更高的要求。在企业里建立一个公平、公开、公正的采购环境&#xff0c;最大限度控制采购成本至关重要。符合国家电子招投标法律法规及相关规范&#xff0c;以及审…