Git Product home page Git Product logo

memoryprofiler's Introduction

Unity是个普及度很高拥有大量开发者的游戏开发引擎,其提供的Unity编辑器可以快速的开发移动设备游戏,并且通过编辑器扩展可以很容易开发出项目需要的辅助工具,但是Unity提供性能调试工具非常简陋,功能简单并且难以使用,如果项目出现性能问题,定位起来相当花时间,并且准确率很低,一定程度上靠运气。

Profiler

目前Unity随包提供的只有Profiler工具,里面聚合CPUGPU、内存、音频、视频、物理、网络等多个维度的性能数据,但是我们大部分情况下只是用它来定位卡顿问题,也就是主要CPU时间消耗(图\ref{profiler})。

Unity性能调试工具Profiler\label{profiler}

CPU的维度里面,可以看到当前渲染帧的函数调用层级关系,以及每个函数的时间开销以及调用次数等信息,但是这个工具同一时间只能处理300帧左右的数据,如果游戏帧率30,那么只能看到过去10秒的信息,并且需要我们一直盯着图表看才有机会发现意外的丢帧情况,这种设计非常的不友好,违反正常人的操作习惯,因为通常情况下如果我要调试游戏内战斗过程的性能开销,首先我要像普通玩家那样安安静静的玩一把,而不是分散出大部分精力去看一个只有10秒历史的滚动图表。这种交互带来两个明显的问题,

  • 由于分心去看Profiler,导致不能全心投入游戏,从而不能收集正常战斗过程的性能数据
  • 为了收集数据需要像正常玩家那样打游戏,不能全神关注Profiler图表,从而不能发现/查看所有的性能问题

上面两个情形相互排斥,鱼与熊掌不可兼得。从这个角度来看,Profiler不是一个好的性能调试工具,苛刻的操作条件导致我们很难发现性能问题,想要通过Profiler定位所有的性能问题简直是痴人说梦。

MemoryProfiler

Unity还提供另外一个内存分析工具MemoryProfiler(图\ref{mp})

Unity内存调试工具MemoryProfiler\label{mp}

在这个界面的左边彩色区域里,MemoryProfiler按类型占用总内存大小绘制对应面积比例的矩阵图,第一次看到还是蛮酷炫的,Unity是想通过这个矩阵图向开发者提供对象内存检索入口,但是实际使用过程中问题多多。

  1. 内存分析过程缓慢
  2. 在众多无差别的小方格里面找到实际关心的资源很难,虽然可以放大缩小,但感觉并没有提升检索的便利性
  3. 每个对象只提供父级引用关系,无法看到完整的对象引用链,容易在跳转过程中搞错上下文关系
  4. 引擎对象的引用和il2cpp对象的引用混为一谈,让使用者对引用关系的理解模糊不清
  5. 没有按引擎对象内存和il2cpp对象内存分类区别统计,加深使用者对内存使用的误解

MemoryProfiler源码托管在Bitbucket,但是从最后提交记录来看,这个内存工具已经超过2年半没有任何更新了,但是这期间Unity可是发布了好多个版本,想想就有点后怕。 MemoryProfiler项目代码更新状态\label{cm}\

有热心开发者也忍受不了Unity这缓慢的更新节奏,干脆自己动手基于源码在github上建代码仓库进行二次开发。 PerfAssist新增特性\label{pf}\

PerfAssistMemoryProfiler源码的基础上做了比较大的更新,主要是增加检索的便利性以及内存快照对比,使用起来比MemoryProfiler初代产品方便了不少,但是由于交互界面的限制,也无法完整展示内存引用关系,内存分析过程依然异常缓慢,甚至会在分析过程中异常崩溃。 内存分析过程中崩溃\label{crash}{width=80%}

鉴于Unity性能调试工具现实存在问题,我觉得亟待开发面向开发者、提供更多维度、更高效率的性能调试工具,于是我开发了UnityProfiler和MemoryCrawler两款工具,分别替代Profiler以及MemoryProfiler进行相同领域的性能调试,它们均使用纯C++实现,因为经过与C#Python语言的测试对比后发现C++有绝对的计算优势,可以非常明显提升性能数据分析效率和稳定性。这两款工具的定位是:降低Unity游戏性能调试的门槛,让拥有不同开发经验的开发者都可以轻松定位各种性能问题,尽管都没有可视化交互界面,不过并不影响分析结果的查看,它们都内置命令行模式的交互方式,并提供了丰富的命令,可以对性能数据做全方位的分析定位。

快速开始

源码编译

  1. github下载源码,并使用Xcode打开工程MemoryCrawler/MemoryCrawler.xcodeproj
  2. Scheme列表选择UnityProfiler,然后按快捷键Comamnd+B编译,该操作会生成命令行工具*/usr/local/bin/UnityProfiler*。
  3. Scheme列表选择MemoryCrawler,然后按快捷键Command+B编译,该操作会生成命令行工具*/usr/local/bin/MemoryCrawler*。

UnityProfiler

  1. 集成UnityEditor脚本,生成数据捕获菜单。 把Editor目录放到Unity工程里面并刷新,之后在UnityEditor菜单里面会出现下图所示的菜单。

  2. 按照官方文档配置真机调试或在Editor环境调试。\

  3. 启动游戏,在需要性能调试的逻辑开始前点击菜单性能/开始采样,并在逻辑结束后点击菜单性能/停止采样\

  4. 启动UnityProfiler分析性能数据。
    在步骤3生成的性能数据保存在工程根目录的ProfilerCapture文件夹里面,执行如下命令开始性能调试会话。\

$ UnityProfiler ProfilerCapture/20190514115025_PERF.pfc 
argc=2
argv[0]=UnityProfiler
argv[1]=ProfilerCapture/20190514115025_PERF.pfc
[0] RecordCrawler::load=28816729
    [1] RecordCrawler::loadStrings=648335
        [2] seek=338165
        [3] read=307367
    [4] RecordCrawler::crawl=27087144
/> func
38.17%    185.12ms #20      ██████████████████████████████████████ WaitForTargetFPS *1
 4.40%     21.36ms #20      ████ Profiler.CollectMemoryAllocationStats *128
 4.38%     21.25ms #200     ████ RenderForward.RenderLoopJob *7
 4.34%     21.02ms #1206    ████ WaitForJobGroup *27
 2.75%     13.32ms #100     ███ SceneCulling *25
 2.03%      9.85ms #100     ██ Camera.Render *3
 1.88%      9.12ms #100     ██ CullResults.CreateSharedRendererScene *46
 1.53%      7.42ms #100     ██ Render.TransparentGeometry *5
 1.52%      7.35ms #320     ██ SkinnedMeshFinalizeUpdate *38
 1.49%      7.21ms #20BehaviourUpdate *76
 /> alloc
[FRAME] index=1036 time=4.250ms fps=235.3 alloc=113 offset=1474
[FRAME] index=1037 time=3.780ms fps=264.2 alloc=113 offset=2683
[FRAME] index=1038 time=4.100ms fps=243.4 alloc=113 offset=3916
[FRAME] index=1039 time=4.030ms fps=248.0 alloc=113 offset=8149
[FRAME] index=1040 time=3.860ms fps=258.7 alloc=113 offset=9358
[FRAME] index=1041 time=31.730ms fps=31.5 alloc=83215 offset=10567
/> frame 1036
[FRAME] index=1036 time=4.250ms fps=235.3 alloc=113 offset=1474
├─Profiler.CollectGlobalStats time=9.527%/0.405ms self=16.080%/0.065ms calls=1 *1
│  ├─Profiler.CollectMemoryAllocationStats time=75.144%/0.304ms self=100.000%/0.304ms calls=1 *2
│  ├─Profiler.CollectAudioStats time=7.752%/0.031ms self=6.700%/0.002ms calls=1 *3
│  │  └─AudioProfiler.CaptureFrame time=93.300%/0.029ms self=84.694%/0.025ms calls=1 *4
│  │     └─AudioProfiler.CaptureChannelGroup time=15.306%/0.004ms self=55.288%/0.002ms calls=1 *5
│  │        └─AudioProfiler.CaptureChannelGroup time=44.712%/0.002ms self=66.866%/0.001ms calls=2 *5
│  │           └─AudioProfiler.CaptureChannelGroup time=33.134%/0.001ms self=100.000%/0.001ms calls=2 *5
│  └─Profiler.CollectDrawStats time=1.025%/0.004ms self=100.000%/0.004ms calls=1 *6
├─EditorApplication.Internal_CallUpdateFunctions() time=0.748%/0.032ms self=99.069%/0.032ms calls=1 alloc=96 *7
│  └─GC.Alloc time=0.930%/0.000ms self=100.000%/0.000ms calls=3 alloc=96 *8
├─UpdateSceneIfNeeded time=0.556%/0.024ms self=9.870%/0.002ms calls=1 *9
│  ├─AudioManager.Update time=87.102%/0.021ms self=77.239%/0.016ms calls=1 *10
│  │  └─AudioSettings.InvokeOnAudioManagerUpdate() time=22.761%/0.005ms self=100.000%/0.005ms calls=1 *11
│  └─FlushDirty time=3.028%/0.001ms self=100.000%/0.001ms calls=1 *12
├─EditorCompilationInterface.IsCompiling() time=0.225%/0.010ms self=96.836%/0.009ms calls=1 alloc=17 *13
│  └─GC.Alloc time=3.164%/0.000ms self=100.000%/0.000ms calls=1 alloc=17 *8
├─editorBeforeUpdate.{ InputUpdate(kInputUpdateEditorBegin); InputSendEvents(); } time=0.125%/0.005ms self=36.696%/0.002ms calls=1 *14
│  ├─NativeInputSystem.NotifyUpdate() time=35.697%/0.002ms self=100.000%/0.002ms calls=1 *15
│  └─NativeInputSystem.NotifyEvents() time=27.607%/0.001ms self=100.000%/0.001ms calls=1 *16
├─UpdatePreloading time=0.048%/0.002ms self=14.809%/0.000ms calls=1 *18
│  └─Application.Integrate Assets in Background time=85.191%/0.002ms self=18.081%/0.000ms calls=1 *19
│     └─Preload Single Step time=81.919%/0.001ms self=100.000%/0.001ms calls=1 *20
├─editorAfterUpdate.{ InputUpdate(kInputUpdateEditorEnd); } time=0.040%/0.002ms self=35.660%/0.001ms calls=1 *21
│  └─NativeInputSystem.NotifyUpdate() time=64.340%/0.001ms self=100.000%/0.001ms calls=1 *15
└─PlayerCleanupCachedData time=0.020%/0.001ms self=63.380%/0.001ms calls=1 *22
   └─CleanUp.TextRenderingGarbageCollect time=36.620%/0.000ms self=100.000%/0.000ms calls=1 *23

MemoryCralwer

  1. 集成UnityEditor脚本,生成数据捕获菜单。 把Editor目录放到Unity工程里面并刷新,之后在UnityEditor菜单里面会出现下图所示的菜单。

  2. 按照官方文档配置真机调试或在Editor环境调试。\

  3. 启动游戏,在需要性能调试的逻辑开始前点击菜单性能/捕获快照

  4. 启动MemoryCrawler分析性能数据。
    在步骤3生成的性能数据保存在工程根目录的MemoryCapture文件夹里面,执行如下命令开始性能调试会话。\

$ MemoryCrawler MemoryCapture/20190515123633_snapshot.pms 
argc=2
argv[0]=MemoryCrawler
argv[1]=MemoryCapture/20190515123633_snapshot.pms
[0] MemorySnapshotReader=180766281
    [1] open_snapshot=78634
    [2] read_header=687494
    [3] readPackedMemorySnapshot=177589621
        [4] read_native_types=367595
        [5] read_native_objects=2205864
        [6] read_gc_handles=120789
        [7] read_connections=731030
        [8] read_heap_sections=114988169
        [9] read_type_descriptions=59152770
        [10] read_virtual_matchine_information=1170
            [11] read_object=543
    [12] postSnapshot=2339963
        [13] create_sorted_heap=31189
        [14] create_type_strings=6193
        [15] read_type_index=1887123
        [16] set_native_type_index=16258
        [17] set_gchandle_index=12052
        [18] set_heap_index=236912
        [19] set_native_object_index=102807
        [20] summarize_native_objects=40163
[0] MemorySnapshotCrawler=71546587
    [1] prepare=2236615
        [2] init_managed_types=865149
        [3] init_native_connections=1366118
    [4] crawlGCHandles=49670327
    [5] crawlStatic=17748112
    [6] summarize_managed_objects=1885572
/> ubar
 47.77  47.77 ████████████████████████████████████████████████ RenderTexture 45541440 #4 *217
 24.75  72.52 █████████████████████████ Texture2D 23599923 #2107 *220
 14.77  87.29 ███████████████ AssetDatabaseV1 14081231 #1 *2
  7.16  94.45 ███████ Font 6824990 #7 *175
  1.26  95.70 █ AudioManager 1197311 #1 *231
  1.14  96.85 █ Shader 1090227 #33 *199
  1.01  97.85 █ MonoScript 960105 #745 *209
  0.90  98.75 █ Mesh 856680 #236 *184
  0.75  99.51 █ AssetBundle 716878 #1 *131
  0.28  99.78 █ Cubemap 263912 #3 *221
  0.05  99.84 █ PluginImporter 51603 #75 *147
  0.04  99.87 █ Material 36040 #35 *182
  0.04  99.91 █ MonoManager 35536 #1 *240
  0.02  99.93 █ MonoBehaviour 21235 #52 *66
  0.01  99.95 █ PlayerSettings 11361 #1 *246
  0.01  99.95 █ InputManager 7332 #1 *238
  0.01  99.96 █ Transform 6760 #13 *119
  0.00  99.97 █ Camera 4704 #2 *22
  0.00  99.97 █ GameObject 3944 #13 *124
  0.00  99.97 █ MonoImporter 3696 #7 *144

下载帮助文档

memoryprofiler's People

Contributors

larryhou avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

memoryprofiler's Issues

Frame的memory info有误?

if (frameIndex == stopFrameIndex) // encode memory info
{
    stream.Write((ushort) (8 * 6));
    stream.Write(Profiler.usedHeapSizeLong); // 1
    stream.Write(Profiler.GetMonoUsedSizeLong()); // 2
    stream.Write(Profiler.GetMonoHeapSizeLong()); // 3
    stream.Write(Profiler.GetTotalAllocatedMemoryLong()); // 4
    stream.Write(Profiler.GetTotalReservedMemoryLong()); // 5
    stream.Write(Profiler.GetTotalUnusedReservedMemoryLong()); // 6
}
else
{
    stream.Write((ushort) 0);
}

Profiler.GetXXXLong()获取的数据和profiler测得的在真机上的memory数据不一致。
考虑使用ProfilerDriver.GetOverviewText(ProfilerArea.Memory, frame_index);来获取内存信息?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.