首页 > Unity3D频道 > 【Unity3D研究院之游戏开发】 > Unity3D研究院自动注入代码统计每个函数的执行效率以及内存分配(九十八)
2018
10-14

Unity3D研究院自动注入代码统计每个函数的执行效率以及内存分配(九十八)

以前用UWA和WeTest的工具来查看代码效率,他们都能提供我们代码每个函数的统计效率,然而并不需要我们做什么,所以我就在想如何实现一个类似他们的功能?

Unity提供了Profiler.BeginSample();Profiler.EndSample();方法来统计代码效率,但是有两个问题

1.只能统计单帧的效率

2.需要手动给每个方法加上这两个方法

然而我想要的是统计每个函数一段时间的所有执行效率的统计,比如玩上游戏大概15分钟,将15分钟内每个方法调用的耗时效率做一个总和出一份报表,(这也是我看UWA和WeTest都有的功能)通过代码注入自动给每个函数的首行和尾行添加两个方法就可以了。

首先在 https://github.com/jbevain/cecil 将mono.cecil取出来,我使用的版本是0.9.6版本,因为我觉得旧版本会稳定一点。另外,我使用的是源代码,没有直接用mono.cecil的DLL。原因是我们项目别的地方使用到了cecil修改的一个版本,为了避免冲突所以使用源代码可以修改命名空间保证两个mono.cecil不会被相互影响。

如下图所示,将mono.cecil导入unity工程即可。

Unity3D研究院自动注入代码统计每个函数的执行效率以及内存分配(九十八) - 雨松MOMO程序研究院 - 1

接着我们就需要写入注入代码了,注入代码测试也可以分为两种。

1.编辑模式下测试,也就是不需要打包。

2.打包测试,这样需要在打包的时候自动将代码注入进去。

先来看看注入的代码

注意这个标签,[PostProcessScene] 如果是正式打包会自动进入该标签下的方法,这样每次打包代码就可以自动注入进去了。

前面我们也提到了注入代码就是在每个方法的首部和尾部自动注入两个函数。如下图所示,注入代码后反编译DLL能看到首行和尾行的代码已经注入进去了。

Unity3D研究院自动注入代码统计每个函数的执行效率以及内存分配(九十八) - 雨松MOMO程序研究院 - 2

代码注入成功以后就可以统计效率了。Begin()的时候取当前的Time.realtimeSinceStartup时间和Profiler.GetTotalAllocatedMemoryLong()内存,然后在End()的时候在取当前的Time.realtimeSinceStartup时间和Profiler.GetTotalAllocatedMemoryLong()内存减去Begin()中之前就记录的值就能统计到每个函数的执行效率和内存分配了,最后将数据生成报表就可以方便查看了。

也可以配合  Profiler.BeginSample() 和Profiler.EndSample() 在Profiler中查看每个函数的消耗。。

这个方法不仅编辑器下可以用,同样可以支持真机IL2CPP和mono我已经测试通过,并且已经用此法优化项目效率啦。最后欢迎大家一起讨论,如有过意见或者建议欢迎在下面给我留言。

参考:http://www.codersblock.org/blog//2014/06/integrating-monocecil-with-unity.html

最后编辑:
作者:雨松MOMO
专注移动互联网,Unity3D游戏开发
捐 赠写博客不易,如果您想请我喝一杯星巴克的话?就进来看吧!

Unity3D研究院自动注入代码统计每个函数的执行效率以及内存分配(九十八)》有 23 条评论

  1. wdp 说:

    PDB文件的怎么处理,在Write的时候出现 System.IO.IOException: Sharing violation on path /Users/fzcs/Downloads2/1-U3d/UnityDemo-master/Unity_Demo1/Library/ScriptAssemblies/Assembly-CSharp.dll

    for (int i = 0; i < assemblies.Length; i++)
    {
    string assemblyPathStr = assemblyPath[i];
    var writerParameters = new WriterParameters();
    writerParameters.WriteSymbols = true;
    var pdbName = Path.ChangeExtension(assemblyPathStr, "pdb");
    if (File.Exists(pdbName))
    {
    var symbolProvider = new PdbWriterProvider();
    writerParameters.SymbolWriterProvider = symbolProvider;
    }
    AssemblyDefinition assembly = assemblies[i];
    assemblies[i].Write(assemblyPathStr, writerParameters);
    }
    我是这样写的

    • pwrxf 说:

      请问你上述问题解决了吗,我也遇到了同样的问题。。。

      • Mumu 说:

        readerParameters.ReadWrite = true;注意ReaderParameters中ReadWrite设为true,同时 assemblyDefinition.Write(assemblyPath, writerParameters);改为 assemblyDefinition.Write(writerParameters);不然会重新打开该文件。

  2. flyflychen 说:

    松哥,我想问下IL2CPP方式不是不会生成dll文件吗?这个方法怎么生效的呢

  3. Black 说:

    System.IO.FileNotFoundException: Could not find file “\Library\ScriptAssemblies\Assembly-CSharp.dll.mdb”
    使用”F:\Program Files\Unity2019.2.16f1\Editor\Data\MonoBleedingEdge\bin\mono.exe” “F:\Program Files\Unity2019.2.16f1\Editor\Data\MonoBleedingEdge\lib\mono\4.5\pdb2mdb.exe” Assembly-CSharp.dll之后报
    Error: A portable PDB can’t be converted to mdb.

  4. hyy 说:

    请问下有时候崩溃是啥问题,找了半天无果

  5. wetew 说:

    我也一样 你后来有解决吗

  6. 许为 说:

    松哥,可以分享下HookUtils文件吗

  7. 许为 说:

    松哥,能分享下HookUtils文件吗

  8. Julian 说:

    替换 Return 跳转的地方写错了,应该用 last,如下。
    var jumpInstructions = methodDefinition.Body.Instructions.Cast().Where(i => i.Operand == last);

  9. Agery 说:

    如何过滤掉调用c++的函数呢?

  10. 刘明 说:

    执行assemblyDefinition.Write保存时报错:System.IO.IOException: Sharing violation on path C:\workspace\sandbox\trunk\code\client\goe\client\11111\xxxx.dll

  11. longyonggang 说:

    程序集浏览器软件 是什么?

  12. Lin Lin 说:

    HookUtils可以分享下吗

  13. mrchen 说:

    松哥 这个HookUtils能一起放出来吗

留下一个回复

你的email不会被公开。