开启辅助访问
 找回密码
 注册帐号

扫一扫,访问微社区

开发者专栏

关注:2006

当前位置:游戏蛮牛 技术专区 开发者专栏

__________________________________________________________________________________
开发者干货区版块规则:

  1、文章必须是图文形式。(至少2幅图)
      2、文章字数必须保持在1500字节以上。(编辑器右下角有字数检查)
      3、本版块只支持在游戏蛮牛原创首发,不支持转载。
      4、本版块回复不得无意义,如:顶、呵呵、不错......【真的会扣分的哦】
      5、......
__________________________________________________________________________________
查看: 1843|回复: 24

[韩宇飞] 【敏捷开发】使用静态分析优化Unity代码

[复制链接]  [移动端链接]

7

主题

10

帖子

82

积分

Rank: 2Rank: 2

UID
67815
好友
2
蛮牛币
481
威望
0
注册时间
2015-1-12
在线时间
21 小时
最后登录
2017-12-13
QQ
发表于 2017-12-5 16:10:45 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有帐号?注册帐号

x
本帖最后由 SilentLion 于 2017-12-12 17:17 编辑

        在Unity开发者大会上听了一个介绍对Unity脚本做静态分析的演讲,感觉非常有用,于是做了一些尝试。大概就是提供了一个基于微软Roslyn插件UnityEngineAnalyzer可以对Unity的代码做静态分析,找到一些代码隐患和一些可能存在的性能问题。插件可以单独运行,也可以导入VS做为一个插件,它取代部分人工的ProjectReview,并可集成到CI系统(持续集成)做自动化检测。

什么是静态代码分析
        简而言之就是指在不运行代码的情况下,通过词法分析,语法分析等技术对代码做扫描和解析,验证代码是否满足规范,是否有隐患,是否可靠可维护等。例如比较著名的Lint可以检测出可能的空指针,拼写错误,除0等等。VS,还有VS的插件Resharper也带有静态代码分析的功能。和一般的插件不同,UnityEngineAnalyzer可以分析一些Unity特有的一些性能和问题隐患,也支持自定义添加规则。         

什么是Roslyn
        Roslyn 是微软公司开源的 .NET 编译器。编译器支持 C# 和 Visual Basic 代码编译,并提供丰富的代码分析API。这是一个更为开放式的编译器,与以往不透明的编译过程不同,开发者可以在编译过程中访问和分析编译数据。根据提供的API可以进行大量用户自定义的扩展。通过使用Roslyn提供的API,在您键入代码时,甚至在您完成一行之前,它们就能生成警告,不需要等到生成代码时才找出您所犯的错误。分析器还可以通过新的Visual Studio 灯泡图标提示来显现自动代码修复方案,让您立即清理代码。

为什么需要静态代码分析
  • 提早发现代码中的BUG,避免将BUG带到生产环境
  • 极大的提高软件质量,以及可维护性
  • 统一代码规范、提高可读性,减少新加入成员的熟悉时间
  • 加速个人和团队的成长,知识和经验的积累
  • 节约有限的开发时间
  • 避免人工检查的遗漏,不可能记住所有优化点也不能保证不遗漏
  • 可以内建到CI系统(持续集成)

[size=14.6667px]

        规范的项目一般会有代码审核,而代码审核分为人工审查和工具审查。人工审查方面目前每天会有代码Review,每个版本会做ProjectReview。但是人都会有疏漏,使用自动化的工具进行补充可以避免人工检查的漏洞和节省大量时间。

UnityEngineAnalyzer的优点
  • 可以发现Unity特有的一些潜在问题,例如代码规范,性能问题
  • 可以作为VS插件使用,也可以作为独立的程序单独执行
  • 可以通过ComandLine执行
  • 可以生成HTML报告
  • 多平台支持

        以下是他们官网的介绍:UnityEngineAnalyzer分析器是一套基于Roslyn分析程序,旨在检测C#代码中的常见问题。Unity3D让我们很容易做跨平台游戏,但是有一些隐藏的规则例如性能和AOT等,会影响到程序的体验和测试等。我们希望这些问题能在编译之前被发现。

UnityEngineAnalyzer的检查项
  • 不允许直接Tag的比较,使用CompareTag代替直接比较减少GC
  • 不允许在Update中调用Find
  • 不允许有空的MonoBehaviour方法,例如UpdateFixedUpdateLateUpdate等;
  • 不允许使用OnGUIOnGUI会导致GC
  • 不允许使用Coroutine避免产生GC,这个目前使用的5.5.1版本已经修复不需要了;
  • 不允许使用Foreach避免产生GC,这个目前使用的5.5.1版本已经修复不需要了;
  • string方法
  • Remoting(AOT)EmitCallsAOT),GetTypeAOT
  • IL2CPP,使用去虚拟化可以提升部分虚函数调用的开销 http://gad.qq.com/article/detail/7168288
  • …….

例子如下:
[AppleScript] 纯文本查看 复制代码
        class FooBehaviour : MonoBehaviour 
        {
                void Start()
                {
                        // AOT0003: Reflection only works for looking up existing types
                        Type.GetType("");
        
                        // UEA0002: Using string methods can lead to code that is hard to maintain
                        SendMessage("");
        
                        // UEA0006: Use of coroutines cause some allocations
                        StartCoroutine("");
                }
        
                // UEA0001: Using OnGUI causes allocations and GC spikes
                void OnGUI()
                {
                }
        
                // UEA0003: Empty MonoBehaviour methods are executed and incur a small overhead
                void FixedUpdate()
                {
                }
        
                void OnTriggerEnter(Collider other)
                {
                        // UEA0004: Using CompareTag for tag comparison does not cause allocations
                        if (other.tag == "")
                        {
                        }
                }
        
                void Update()
                {
                        // UEA0005: Warning to cache the result of find in Start or Awake
                        GameObject.Find("");
                }
        }
        
  • 命令行模式
    • https://github.com/vad710/UnityEngineAnalyzer 下载最新版本,然后解压缩,编译。UnityEngineAnalyzer.CLI\bin目录下得到可执行文件UnityEngineAnalyzer.CLI.exe
    • 打开命令提示符或Powershell窗口
    • 运行UnityEngineAnalyzer.CLI.exe <project path>。例如:> UnityEngineAnalyzer.CLI.exe C:\Code\MyGame.CSharp.csproj
    • 观察分析结果
    • 在项目文件相同位置,会生成report.json和UnityReport.html报告
1.png
        上图是我们项目的分析报告。需要注意的是html需要用FireFox浏览器打开,json可以直接浏览 。原始工程的exe在执行的时候会报一些异常需要修改一下代码,主力工程不需要。命令行模式设置可以通过选择项目-〉右键-〉属性;调试-〉命令行参数。然后在命令行参数里面输入命令行参数调试运行。


  • NuGet打包
        有两种方式可以制作插件集成到VS工程中,一个就是以NuGet package的方式。因为在网上已经有编译好的NuGet包,我只尝试了直接使用并没有编译本地的的NuGet。安装步骤如下:
  • 使用VS2015打开目标工程
  • 选择Tools->NuGet     Package Manager->Manage NuGet Package for solution,如下图:

    2.png
  • Browse页面搜索UntiyEngineAnalyzer并选中
  • 在右侧属性面板中勾选你希望分析的项目点击Install。(注意源的版本比较老只能添加一个工程)
3.png


  • VSIX扩展包
          另一种方式是编译成VSIX插件,安装到VS中对IDE下所有项目进行分析。只要编译UnityEngineAnalyzer.Vsix工程在bin中双击UnityEngineAnalyzer.Vsix.vsix安装即可。安装完毕可以在Tools/Extensionsand Updates里看到,如下图:
4.png
重启VS之后在Error List就可以看到用户自定义的Unity特有的Wanrings了,如下图:
5.png
因为NuGet源上的版本非常老了而且使用没有VSIX方便,所以最终使用了VSIX的方式,给其他人提供了VSIX的插件。

UnityEngineAnalyzer的扩展
           我们当然可以对分析器进行扩展和修改。例如新版本的Unity不需要对Foreach等做限制,另外可以添加项目组自己的代码规范,代码风格,特定设置等。
  • 安装Visual Studio 2015或者更高版本
  • 安装Roslyn_SDK
https://marketplace.visualstudio.com/items?itemName=VisualStudioProductTeam.NETCompilerPlatformSDK

解决方案中的工程如下:
  • UnityEngineAnalyzer:这是主项目,构建包含诊断和代码修补程序的分析器     DLL
  • UnityEngineAnalyzer.CLI:这是可执行的exe
  • UnityEngineAnalyzer.Test:这是单元测试项目,有一些测试用例便于调试
  • UnityEngineAnalyzer.Vsix:这是制作用于VS的插件的项目,也是把dll绑定到VS
安装完SDK之后VS有相应的模版可以创建工程,这里没有这个需求只是在源工程上修改。

        如果通过模版创建会默认生成DiagnosticAnalyzer代码,这里每条分析都一个派生类继承自DiagnosticAnalyzer。例如DoNotUseOnGUIAnalyzer。在DiagnosticIDs这个文件里可以自定义一些用户自己希望使用的警告标签,例如DoNotUseOnGUI= "UEA0001" AnalyzerTestFixture是测试用例的基类。
        通过设置UnityEngineAnalyzer.Vsix启动启动,按F5启动可以启动一个VS副本用来调试。这和安装完VS第一次启动一样,还需要设置一些参数风格等。如下图右边的VS通过调试模式启动左边的VS,这是在打断点调试。
6.png
       如下图可以看到副本的VS中警告部分会有绿色下划线,下方会有warning的标签,原因提示等,双击也可以调到有问题的代码的地方。file:///C:/Users/HANYUF~1.LIL/AppData/Local/Temp/msohtmlclip1/02/clip_image006.png
8.png   
        然后我们看一下如何进行扩展。首先第一步继承Initialize函数,这是分析器的入口函数,通过注册一个回调来进行诊断。例如context.RegisterSymbolAction(AnalyzeSymbol,SymbolKind.Method);其中AnalyzeSymbol是要实现的检查的回调函数,SymbolKind.Method是检测类型,后面再可视化视图中可以看到节点类型。每次启动的时候会调用一次Initialize。

        当每次在VS中键入代码的时候,都会调用注册的回调函数AnalyzeSymbol(用户可以自己定义)。当检测发现问题的时候则调用context.ReportDiagnostic(diagnostic)方法,抛出一个警告。参数diagnostic可以设置警告的标签,警告的内容,行号,类名等等。用户可以通过context获取节点的一些信息,例如函数名是否是OnGUI,如果不是则返回。如果是继续判断该函数的类是否是Unity的MonoBehaviour类的派生类。代码如下:
[AppleScript] 纯文本查看 复制代码
                private static void AnalyzeSymbol(SymbolAnalysisContext context)
                {
                    // check if the method is name OnGUI
                    var methodSymbol = context.Symbol as IMethodSymbol;
                    if (!methodSymbol.Name.Equals("OnGUI")) { return; }
        
                    // check that it is contained in a class extended by UnityEngine.MonoBehaviour
                    var containingClass = methodSymbol.ContainingType;
                    var baseClass = containingClass.BaseType;
                    if (baseClass.ContainingNamespace.Name.Equals("UnityEngine") &&
                        baseClass.Name.Equals("MonoBehaviour"))
                    {
                        var diagnostic = Diagnostic.Create(DiagnosticDescriptors.DoNotUseOnGUI, methodSymbol.Locations[0], containingClass.Name);
                        context.ReportDiagnostic(diagnostic);
                    }
        }

        DiagnosticDescriptors是一个静态类,它的作用就是创建一些静态的DiagnosticDescriptor,用来设置标签和内容等,字符串等放在ResourceManager中,然后添加到ImmutableArray<DiagnosticDescriptor>SupportedDiagnostics。
       选择View->Other Windows->Syntax Tree,可以打开语法分析窗口,将鼠标放在代码的某一行上可以得到语法树的可视化信息。如下图:
9.png
        通过在编辑器中选择代码,您可以看到树中的相关节点,反之亦然。还可以在语法树中右键单击“ViewDirected Syntax Graphic”生成一个图,可视化所选节点的树结构,可以看到语法令牌、各个词、数字和符号等。例如Initialize函数,如图 4 中所示。
10.png
        如果右键选择“View Symbol”,可以在下面的属性网格将显示所调用方法的方法符号信息,可以看到Initialize的调用是UnityEngineAnalyzer.AOT.TypeGetTypeAnalyzer.Initialize(Microsoft.CodeAnalysis.Diagnostics.AnalysisContext),如下图:
11.png
        打开DoNotUseOnGUIResources.resx文件,可以设置在错误列表中向用户显示标题,描述等。使用者还可以使用 #pragma 指令禁止该诊断的某个测试。,如下图:
12.png
        还可以通过设置defaultSeverity:DiagnosticSeverity.Info设置将要生成的诊断的严重程度,例如Info,Warning,Error等。如果
改为Error在VS中则会中断编译。另外也可以定义是否在默认情况下关闭或打开,可以选择加入部分或者全部规则。例如:
13.png
        SupportedDiagnostics属性可以添加单个或多个诊断。一般情况一个分析器应该只生成一种诊断,但有的时候一个分析会区分在不同情况下抛出不同的诊断,例如DoNotUseFindMethodsInUpdate和DoNotUseFindMethodsInUpdateRecursive。
         除了可以发现编码中的错误,另外还可以编写代码来提供修改代码的方案。这个暂时还没有尝试。
         附上其他的一些Register的方法。
QQ截图20171212171109.png

UnityEngineAnalyzer的总结
  • 静态代码分析不能代替性能分析和Review等人工检查,但可以节省大量时间和将部分过程自动化流程化
  • 可以从性能分析中得到优化点不断添加到新的分析器中
  • 只能优化代码
  • 可根据需求或者Unity版本升级,随时修改Analyzer
  • 配合项目的0Error0warning计划极大程度提高代码稳定性
  • Mac下还不支持,还不能集成到Unity编辑器中
  • 不同平台制定不同的优化策略。例如IL2cpp的反虚拟化优化,减少虚函数的调用开销


如何获得UnityEngineAnalyzer
  • 当前活跃项目: https://github.com/vad710/UnityEngineAnalyzer
  • 原始项目: https://github.com/meng-hui/UnityEngineAnalyzer
  • 其他项目:https://bitbucket.org/kzoabi/unity3d-gendarme-plugin


回复

使用道具 举报

7日久生情
2970/5000
排名
2113
昨日变化
10

0

主题

1891

帖子

2970

积分

Rank: 7Rank: 7Rank: 7Rank: 7

UID
94526
好友
0
蛮牛币
1714
威望
0
注册时间
2015-4-22
在线时间
537 小时
最后登录
2017-12-13
发表于 2017-12-5 17:27:18 | 显示全部楼层
感谢楼主分享

回复

使用道具 举报

7日久生情
1525/5000
排名
5628
昨日变化
46

2

主题

1119

帖子

1525

积分

Rank: 7Rank: 7Rank: 7Rank: 7

UID
241666
好友
0
蛮牛币
6002
威望
0
注册时间
2017-9-6
在线时间
208 小时
最后登录
2017-12-13
发表于 7 天前 来自Mobile--- | 显示全部楼层
感谢分享

回复

使用道具 举报

7日久生情
3280/5000
排名
4934
昨日变化
36

4

主题

2740

帖子

3280

积分

Rank: 7Rank: 7Rank: 7Rank: 7

UID
209186
好友
5
蛮牛币
3787
威望
0
注册时间
2017-3-1
在线时间
306 小时
最后登录
2017-12-13
发表于 7 天前 | 显示全部楼层
谢谢分享

回复

使用道具 举报

排名
2753
昨日变化
9

28

主题

328

帖子

1590

积分

Rank: 9Rank: 9Rank: 9

UID
148923
好友
46
蛮牛币
1758
威望
0
注册时间
2016-5-17
在线时间
490 小时
最后登录
2017-12-12

专栏作家

QQ
发表于 7 天前 | 显示全部楼层
赞个

回复

使用道具 举报

4四处流浪
436/500
排名
9443
昨日变化
108

0

主题

245

帖子

436

积分

Rank: 4

UID
146677
好友
8
蛮牛币
1987
威望
0
注册时间
2016-4-25
在线时间
101 小时
最后登录
2017-12-12
QQ
发表于 7 天前 | 显示全部楼层
这是个好东西,谢谢分享,以前很多都人工检查太累

回复 支持 反对

使用道具 举报

6蛮牛粉丝
1442/1500
排名
2841
昨日变化
14

7

主题

645

帖子

1442

积分

Rank: 6Rank: 6Rank: 6

UID
83438
好友
2
蛮牛币
2488
威望
0
注册时间
2015-3-23
在线时间
370 小时
最后登录
2017-12-13
发表于 7 天前 | 显示全部楼层
感谢分享 提供思路了

回复 支持 反对

使用道具 举报

6蛮牛粉丝
1020/1500
排名
2696
昨日变化
1

3

主题

138

帖子

1020

积分

Rank: 6Rank: 6Rank: 6

UID
45560
好友
1
蛮牛币
27
威望
0
注册时间
2014-9-18
在线时间
445 小时
最后登录
2017-12-13
发表于 7 天前 | 显示全部楼层
不错不错,学习了

回复

使用道具 举报

5熟悉之中
617/1000
排名
3516
昨日变化
21

10

主题

67

帖子

617

积分

Rank: 5Rank: 5

UID
79517
好友
0
蛮牛币
5661
威望
0
注册时间
2015-7-10
在线时间
214 小时
最后登录
2017-12-13
发表于 7 天前 | 显示全部楼层
先收藏了 有时间再看

回复 支持 反对

使用道具 举报

2初来乍到
106/150
排名
15851
昨日变化
13

0

主题

40

帖子

106

积分

Rank: 2Rank: 2

UID
229553
好友
0
蛮牛币
216
威望
0
注册时间
2017-6-30
在线时间
32 小时
最后登录
2017-12-13
发表于 7 天前 | 显示全部楼层
收藏一下!!!

回复

使用道具 举报

3偶尔光临
290/300
排名
8486
昨日变化
74

0

主题

117

帖子

290

积分

Rank: 3Rank: 3Rank: 3

UID
229748
好友
0
蛮牛币
381
威望
0
注册时间
2017-7-1
在线时间
67 小时
最后登录
2017-12-12
发表于 7 天前 | 显示全部楼层
收藏一下吧

回复

使用道具 举报

4四处流浪
315/500
排名
10523
昨日变化
6

6

主题

92

帖子

315

积分

Rank: 4

UID
5349
好友
1
蛮牛币
1703
威望
0
注册时间
2013-10-10
在线时间
159 小时
最后登录
2017-12-12
发表于 7 天前 | 显示全部楼层
非常有用。。~~
[发帖际遇]: d_kb 发帖时在路边捡到 1 蛮牛币,偷偷放进了口袋. 幸运榜 / 衰神榜

回复

使用道具 举报

3偶尔光临
231/300
排名
10523
昨日变化
171

0

主题

89

帖子

231

积分

Rank: 3Rank: 3Rank: 3

UID
243817
好友
0
蛮牛币
129
威望
0
注册时间
2017-9-16
在线时间
68 小时
最后登录
2017-12-12
发表于 7 天前 | 显示全部楼层
感谢楼主分享

回复

使用道具 举报

5熟悉之中
775/1000
排名
4247
昨日变化
31

0

主题

295

帖子

775

积分

Rank: 5Rank: 5

UID
220310
好友
1
蛮牛币
1302
威望
0
注册时间
2017-5-2
在线时间
206 小时
最后登录
2017-12-13
发表于 7 天前 | 显示全部楼层

回复

使用道具 举报

4四处流浪
474/500
排名
9562
昨日变化
115

11

主题

317

帖子

474

积分

Rank: 4

UID
249218
好友
2
蛮牛币
1544
威望
0
注册时间
2017-10-17
在线时间
52 小时
最后登录
2017-12-13
发表于 6 天前 | 显示全部楼层
zzzzzz zzzzzzzz

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册帐号

本版积分规则

快速回复 返回顶部 返回列表