让MemoryProfiler变得更强
之前其实在C#反射——掀起Unity Editor面纱里提到过Profiler的增强;后来官方提供了更加方便的MemoryProfiler,上周末的时候顺手用类似的思路增加了diff功能并进行了一定的优化。原来那个ProfilerDiff就直接废弃,毕竟当时懒了没做序列化
ps. 本来我想直接用gulu大大的PA_ResourceTracker,不过仔细研究了下代码发现需要接入的东西比较多(很多数据信息是通过网络传输的),嵌入我们自己的项目不是特别方便。最后自己动手弄个山寨版,也就半个多小时搞定…
ps. 对于数据的解读参考Unity MemoryProfiler 的工作机制及可能的改进即可,这里不再赘述。
快照对比功能
我这里做了一个偷懒做法,直接对比了NativeUnityEngineObject
里的数据,规则比较简单:
- 如果两个物体的
instanceID
相同:- 如果大小也相同,那么就表示没有变化,不予显示
- 如果大小不同,那么作差得到变化值
- 剩下的按照名字排序,然后按照大小排序:如果有大小相同的同名项继续『抵销』(这一步其实是有点问题的,不过为了专注于差值就先这么干了)
- 最后剩下的就是新增的或者释放的
最后将生成的数据塞回去显示,红绿分别代表new/delete就非常显眼了(见teaser)。由于相关代码都是开源的,所以改起来特别方便,不用像之前反射扩展Profiler那么麻烦。
不过这里有一个缺陷我没有完全解决干净:同一个资源的instanceID
是会变的,也就是说重启Unity Editor之后就不好做diff了…后面我在想要不要直接参考名字算了,不过这也是有风险的(譬如同名同大小但是实际上是不同的两个资源会被误判)。
优化序列化速度
MemoryProfiler内部其实已经实现了三套序列化机制
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
UnityEngine.JsonUtility.ToJson
Newtonsoft.Json.JsonSerializer
默认用的是第三个(相对最快的一个),但说实话这仨都非常慢。这里我用了一个粗暴的手写来解决:
bw.Write(snapshot.nativeTypes.Length); |
比较麻烦的地方在于读取,因为这些类只提供了getter——那么解决方案已呼之欲出,反射走起。
int len = br.ReadInt32(); |
这里有一点需要注意的是直接去SetValue
无效,因为有个装箱的问题,具体可以参考Is there a way to set properties on struct instances using reflection?。
我测试了下,保存的速度轻松快了10x,读取的速度也有5x的提升…
TODO
这些都是我们在使用过程中遇到的一些可以改进的地方,其实仔细想想还有不少继续扩展,特别是一些已经在PA_ResourceTracker里实现了:
- Daily Build Performance Report
- Search
- Table View
嘛反正挖坑不填是常态,以后有精力再说(逃