[程式] 了解UE4同步传输的开销(Bunch Overhead)

楼主: yekdniw (yekdniw)   2019-06-27 22:23:09
网页版
https://yekdniwunrealengine.blogspot.com/2019/06/BunchOverhead.html
这篇文章其实算是Network Profiler (一) (二)的后续,
主要是利用Profiler分析的过程中发现replicate property已经减少很多,
可是total send Bytes没有如预期的下降到目标,
经过追查研究后,发现问题在Bunch Overhead后,
才有了这一篇文章的内容
Bunch Overhead
如果降replicate传输到一定程度之后,会发现其实Bunch Overhead蛮大的。
从Profiler里面可以看到Bunch Overhead分为
★Bunch Headers
★ContentBlock Headers
★Content Footers
★Handles
★Export GUIDS
★MustBeMapped GUIDS
这几个项目
如图1.所示
[图1.]
图1. BunchOverhead的细项可在Network Profiler的Summary内找到
其中我只稍微追查Bunch Headers, Handles, Export GUIDS这几项而已,
其他项目因为传输不多所以我没有深入追。
Bunch Headers
Bunch Header到底传了那些,可以在
Engine/Source/Runtime/Engine/Private/NetConnection.cpp
的UNetConnection::SendRawBunch() 里面追查到
大致上就是这个Bunch 是open/close,是不是reliable,
channel index是多少等等的资讯
Bunch headers的资料量从我们开发端是很难省掉的,
不过引擎端因为Fornite持续在开发的关系,应该会不断地改进。
例如以前有Bunch.bIsDormant现在直接被合并进
enum EChannelCloseReason Bunch.CloseReason。
Export GUIDS
这个项目主要发生在Server 要同步一个新的Actor给client的时候,
需要利用GUID跟client建立对应关系。
如果是动态生成的物件,会直接使用物件的路径+名称,以字串的方式作为GUID传送
举例来说server生成一个新的需要同步的Actor
路径在Content/Gameplay/Character/Skill/BP_bbb
那么生成这个actor就会产生Export GUIDS的项目
在network profiller可以看到,大小至少是
Gameplay/Character/Skill/BP_bbb 31个Bytes。
会说至少是因为前后还会再加上Prefix以及Suffix,所以实际会更多。
除此之外,如果BP_bbb这个Actor的component也勾了component replicate的话
这个component也会产生Export GUIDS的项目。
也就是说如果你的游戏很频繁的动态生成物件,那么Export GUIDS这个项目会很高。
但是Export GUIDS是可以透过物件池改善的。
同一个Actor可以重复使用的话就不用传GUID。
当然网络版本的物件池如何同步状态又是另一个难题了~
有关Export GUIDS输出的程式码大约是在
Engine/Source/Runtime/Engine/Private/PackageMapClient.cpp
UPackageMapClient::SerializeNewActor->
UPackageMapClient::SerializeObject->
UPackageMapClient::InternalWriteObject
有需要可以从这几个地方开始追。
Handles
FNetworkProfiler::TrackWritePropertyHandle这个函式就是
用来处理Handles的项目,而呼叫的地方都是从
Engine/Source/Runtime/Engine/Private/RepLayout.cpp的
WritePropertyHandle触发的
如果搜寻程式码的话就可以知道Handle就是Server在Replicate Actor
replicate变量的时候,用来告诉client后续的资料是哪个变量的
举例来说BP_bbb有三个可同步的integer变量var1, var2, var3
如果只有var2有改变,var2的值从0变成1
那server 就需要送类似 BP_bbb (var2, 1) 这样的资料给Client。
而var2要怎么表示让client知道,就是由Handle负责。
基本上就是每个变量依照顺序给编号,所以var1=1, var2=2, var3=3
经由转换后就是 BP_bbb (2, 1)
不过因为server需要让client知道property资料传完了,
所以最后还要传编号0作为property的结束
也就是BP_bbb (2, 1, 0)
Array property的情况就更复杂了
除了array property的index,最后也要加上编号0作Array结束的识别证明。
详细传Array的过程的额外细节我就没有特别追查,
总之记得成本比一般的property多就对了。
Handle的传输细节
Handle的变量宣告为uint16,所以如果我们传var2的资料(2, 1),
大小会是多少呢?
在实际输出Handle的时候,因为呼叫了
Writer.SerializeIntPacked(LocalHandle)
所以不会每次输出都是2+4 Bytes。但是还是有一定的大小
输出的实际程式码在
Engine/Source/Runtime/Core/Private/Serialization/BitWriter.cpp
FBitWriter::SerializeIntPacked(uint32& InValue)
里面,有需要可以追一下。
Worst Case
上述的范例 假设handle是1 Byte
那么传递一个integer实际上是
4/(4+1) = 80%
等于有20%是overhead
如果我们要同步的变量只有1bit的boolean呢?
最后可能要传9Bits
1/ (8+1) = 11%
等于接近90%的传输都是overhead...
贵爆!
改善Handle的Overhead
有一个作法可以避免replicate每个property前面都要带一个handle
那就是把多个property包成一个structure
并且实作这个structure的NetSerialize()
如此一来 这多个property的传输只会共用一个handle来处理
范例
http://www.aclockworkberry.com/custom-struct-serialization-for-networking-in-unreal-engine/
可以参考这篇文章来实作。或是直接搜寻引擎程式码,有很多范例
缺点
整个structure实作NetSerialize后有一个重大的缺点
就是在计算变动的时候也是以整个structure作内存比较
所以整个structure如果只要有一个变量变动
也会呼叫NetSerialize并输出。
原来的版本因为各个property拆开,所以每个property会独自作内存比较再输出
所以如果整个structure常常只有少部分变动的话,
有可能实作NetSerialize反而传输会变大。
实际上还是要在没实作NetSerialize+Handles跟实作可能会浪费,
取得平衡点。
追踪SerializeNewActor传输开销
除了Bunch Overhead属于比较隐密容易被漏掉的项目
Server spawn一个新的actor所需要的传输量,
其实在Network Profiler没有页面显示出来
除了从程式码可以稍微知道基本要传哪些资讯 ,
最后实际上同步新的actor的资料量其实是非常难知道的
这边列出几个我知道的项目
★GUIDS
★Transform与速度
★Replicate Properties
GUIDS 前面有提过,这个是NetworkProfiler内就能看到的项目
Transform, Rotation, Scale, Velocity等项目是要传输,
但无法在Profiler内找到的。
最后是这个actor需要replicate的各个变量,如果与默认值不同就会传输。
这部份在NetworkProfiler可以看的到。
程式码可以看到在传各个Transform, Rotation, Scale. Velocity之前
会先给一个bit告知是不是默认值,如果是默认值的话就直接不传了。
这个传输技巧还蛮值得学习,可以应用在NetSerialize实作的时候,
搜寻引擎程式码也会看到这个技巧被大量应用在各个需要传输的地方。
然后因为Transform, Rotation等等都有自己的NetSerialize版本。
所以传输的资料量是变动的,造成SerializeNewActor的成本很难预估。
另一个值得注意的是Vector是使用FVector_NetQuantize10,
Rotation是使用FRotator。
平常有需要同步座标或旋转的时候,记得使用这两种类型来降低传输。
以上就是有关网络传输开销的内容
Gameplay Network 传输相关的内容会暂时告一段落,目前没有新的项目要分享。
不过如果有想知道UE4的课题,也欢迎让我知道,谢谢~
作者: coolrobin (泳圈)   2019-06-28 21:00:00
同样未看先推
作者: adm123 (Administrator)   2019-06-29 10:53:00
你有FB吗?想订阅
楼主: yekdniw (yekdniw)   2019-07-02 01:23:00
抱歉 可能没时间经营FB~但是我文章会出没在discord跟PTT

Links booklink

Contact Us: admin [ a t ] ucptt.com