查看原文
其他

在编辑器工具中快速提取类型属性

Unity Unity官方平台 2022-05-07

如果你针对Unity 2019.2开发编辑器实用功能或资源包,可以使用UnityEditor.TypeCache API处理类型提取过程,以减少工具的初始化时间,进入运行模式的时间以及域重载的时间。


产生性能问题的原因

在调查和优化进入运行模式的过程时,我们发现从加载的程序集提取类型需要很长的时间。


型提取在内部被编辑器模块广泛使用,在外部被资源包和用户代码用于扩展编辑功能。积累效果根据项目而不同,可能会给域重载时间增加300-600毫秒,如果系统具有延迟初始化,它会增加更多时间。

 

在新的Mono运行时中,由于Type.IsSubclassOf的性能下降问题,所需时间会大幅提升,最多可达1300毫秒。

 

产生性能问题的原因是,代码通常会从当前域提取所有类型,然后迭代所有类型,执行耗费性能的检查。所需时间会根据游戏拥有的类型数量而线性增加,通常游戏的类型数量为3~6万。


解决方法

缓存类型信息允许我们减少在域中迭代类型产生的O(N)复杂度。在本地层级,我们已经有了加速结构,它们会在程序集加载后填充,并缓存类型数据,例如:方法,类属性和接口实现。


在内部,这些结构会通过UnityEditor.EditorAssemblies API公开,以利用快速缓存功能。然而,该API并没有公开发布,而且也不支持重要的SubclassesOf用例。

 

在Unity 2019.2中,我们优化并扩展了本地缓存,并将其作为公开的UnityEditor.TypeCache API。它可以非常快速的提取信息,允许仅对我们感兴趣的少量类型进行迭代,一般为10-100个,这样可以显著降低编辑器工具获取类型所需的时间。

public static class TypeCache

   {

       public static TypeCollection GetTypesDerivedFrom < T > ();

       public static TypeCollection GetTypesWithAttribute < T > () 其中 T : Attribute ;

       public static MethodCollection GetMethodsWithAttribute < T > () 其中 T : Attribute ;


       public struct MethodCollection : IList < MethodInfo > { ... }


       public struct TypeCollection : IList < Type > { ... }

   }


我们在本地拥有的基础数据由数组来表示,并且它对于域生命周期是不可改变的。因此,我们可以使用返回IList<T>接口的API,它会实现为本地Dynamic_array数据的视图,它会给我们提供:

 

  • IEnumerable(foreach和LINQ)的灵活性和可用性。

  • 使用for (int i)进行快速迭代

  • 快速转换为List<T>和Array


使用示例


下面查看几个示例。

 

通常,查找接口实现的代码如下:

static List<Type> ScanInterfaceImplementors(Type interfaceType)

{

    var types = new List<Type>();

    var assemblies = AppDomain.CurrentDomain.GetAssemblies();

    foreach (var assembly in assemblies)

    {

        Type[] allAssemblyTypes;

        try

        {

            allAssemblyTypes = assembly.GetTypes();

        }

        catch (ReflectionTypeLoadException e)

        {

            allAssemblyTypes = e.Types;

        }

 

        var myTypes = allAssemblyTypes.Where(t =>!t.IsAbstract && interfaceType.IsAssignableFrom(t));

        types.AddRange(myTypes);

    }

    return types;

}


通过TypeCache API,只需使用以下代码:

TypeCache.GetTypesDerivedFrom<MyInterface>().ToList()


类似的,查找用属性标记的类型需要以下代码:

static List<Type> ScanTypesWithAttributes(Type attributeType)

{

    var types = new List<Type>();

    var assemblies = AppDomain.CurrentDomain.GetAssemblies();

    foreach (var assembly in assemblies)

    {

        Type[] allAssemblyTypes;

        try

        {

            allAssemblyTypes = assembly.GetTypes();

        }

        catch (ReflectionTypeLoadException e)

        {

            allAssemblyTypes = e.Types;

        }

        var myTypes = allAssemblyTypes.Where(t =>!t.IsAbstract && Attribute.IsDefined(t, attributeType, true));

        types.AddRange(myTypes);

    }

    return types;

}


通过使用TypeCache API,只需要一行代码:

TypeCache.GetTypesWithAttribute<MyAttribute>().ToList();

性能

如果我们使用性能测试框架编写简单的性能测试,我们可以清楚看到使用TypeCache API的优势。


在一个空白项目中,我们可以在域重载后节省超过100毫秒的时间。

 

未来在Unity 2019.3中,TypeCache.GetTypesDerivedFrom也支持把泛型类和接口作为参数。大部分编辑器代码已经转换为TypeCache API。


所以,我们希望用户尝试使用TypeCache API,你的反馈将帮助我们使编辑器变得更快。

 

如果你开发的编辑器实用功能或资源包需要扫描域中的所有类型,以使其变得可定制,请考虑使用UnityEditor.TypeCache API,使用它的积累效果会显著降低域重载时间。


小结

了解UnityEditor.TypeCache API:

https://docs.unity3d.com/2019.2/Documentation/ScriptReference/TypeCache.html


更多Unity 2019.2新功能介绍,尽在Unity Connect平台(Connect.unity.com)。


下载Unity Connect APP,请点击此处。 观看部分Unity官方视频,请关注B站帐户:Unity官方


推荐阅读

Unity 2019.2 Beta版发布

Unity编辑器扩展工具,打造Unity为建模利器

轻松玩转Unity编辑器菜单扩展

AR Foundation现已支持ARKit 3

Unity Reflect:一键转换BIM数据至实时3D环境

使用Unity实现魔法火焰效果


喜欢本文,请点击“在看”

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存