精华内容
下载资源
问答
  • IL2CPP

    2016-10-07 22:30:00
    [官方教程] IL2CPP 深入讲解系列介绍 (汇总帖) http://forum.china.unity3d.com/forum.php?mod=viewthread&tid=8960&highlight=il2cpp Unity将来时:IL2CPP是什么?...

    [官方教程] IL2CPP 深入讲解系列介绍 (汇总帖)

    http://forum.china.unity3d.com/forum.php?mod=viewthread&tid=8960&highlight=il2cpp

     

    Unity将来时:IL2CPP是什么?
    http://inpla.net/thread-8197-1-1.html
    https://zhuanlan.zhihu.com/p/19972689


    Unity3D将来时:IL2CPP(下)
    http://blog.csdn.net/qq168213001/article/details/46319219

    Unity3D 降低IL2CPP编译可执行文件大小
    http://www.cnblogs.com/mrblue/p/4979004.html
    Suggestion for reducing the size of IL2CPP generated executable
    http://forum.unity3d.com/threads/suggestion-for-reducing-the-size-of-il2cpp-generated-executable.338986/


    用Unity制作游戏,你需要深入了解一下IL2CPP
    http://blog.csdn.net/qq168213001/article/details/46319139

    Unity开发-IL2CPP概述
    http://blog.sina.com.cn/s/blog_7cbd10170102voxc.html

    AN INTRODUCTION TO IL2CPP INTERNALS
    https://blogs.unity3d.com/2015/05/06/an-introduction-to-ilcpp-internals/

    IL2CPP 深入讲解:垃圾回收器的集成
    http://www.jianshu.com/p/59d3ab1135b3

    Unity3D Native 插件开发: IL2CPP 机制篇
    http://tieba.baidu.com/p/4566737560

    IL2CPP 深入讲解:P/Invoke封装(三)
    http://blog.csdn.net/gz_huangzl/article/details/52485507

    IOS平台Unity引擎的IL2CPP机制分析及安全性评估
    http://gad.qq.com/article/detail/7166024

    从Profile中窥探Unity的内存管理
    http://blog.csdn.net/cxihu/article/details/51538542

    (二)Unity5.0新特性------unity内部:内存 和 性能(以及Unity5的升级优化)
    http://blog.csdn.net/u010019717/article/details/44758685?locationNum=6

    IL2CPP 深入讲解:垃圾回收器的集成(六)
    http://blog.csdn.net/gz_huangzl/article/details/52485545

    技术干货|IL2CPP 深入讲解系列介绍之4:方法调用介绍
    http://www.wtoutiao.com/p/Db8sho.html

    转载于:https://www.cnblogs.com/javalzy/p/5936981.html

    展开全文
  • frida-il2cpp 那些想玩Unity il2cpp游戏的人的帮助库。 在Windows上进行了测试,但也可以轻松在Android / iOS上使用。 我认为只有Process.findModuleByName("GameAssembly.dll")!; il2cpp.ts需要更改。 示例类 ...
  • il2cpp 我有时间学习SCPSL游戏中的megapatch2更改。 从现在开始,旧仓库是只读的。 预习 该项目是il2cpp游戏黑客的基础。 您可以进行一些更改以使其可以与任何其他游戏一起使用,而不仅限于SCPSL。 更新 v0.4:挂...
  • il2cpp源码

    2019-04-22 04:12:24
    从Unity3D目录中提取的il2cpp部分的源码,内含il2cpp部分的所有细节源码
  • il2cpp 在我们的IL2CPP微型优化迷你系列的最后一集中,我们将探讨被称为“装箱”的高成本,并且我们将了解IL2CPP如何避免不必要的操作。 (In this final episode of our IL2CPP micro-optimization miniseries, we’...

    il2cpp

    In this final episode of our IL2CPP micro-optimization miniseries, we’ll explore the high cost of something called “boxing”, and we’ll see how IL2CPP can avoid it when it is done unnecessarily.

    在我们的IL2CPP微型优化迷你系列的最后一集中,我们将探讨被称为“装箱”的高成本,并且我们将了解IL2CPP如何避免不必要的操作。

    堆分配很慢 (Heap allocations are slow)

    Like many programming languages, C# allows the memory for objects to be allocated on the stack (a small, “fast”, scope-specific, block of memory) or the heap (a large, “slow”, global block of memory). Usually allocating space for an object on the heap is much more expensive than allocating space on the stack. It also involves tracking that allocated memory in the garbage collector, which has an additional cost. So we try to avoid heap allocations where possible.

    像许多编程语言一样,C#允许将对象的内存分配到堆栈(较小的,“快速的”,特定于作用域的内存块)或堆(较大的,“慢的”,全局内存块)上。 通常,在堆上为对象分配空间比在堆栈上分配空间要昂贵得多。 它还涉及在垃圾回收器中跟踪已分配的内存,这会产生额外的费用。 因此,我们尝试在可能的情况下避免堆分配。

    C# lets us do this by separating types into value types (which can be allocated on the stack), and reference types (which must be allocated on the heap). Types like int and float are value types, string and object are reference types. User-defined value types use the struct keyword. User-defined reference types use the class keyword. Note that a value type can never hold a the value null. In C#, the null value can only be assigned to reference types. Keep this distinction in mind as we continue.

    C#通过将类型分为 值类型 (可以在堆栈上分配)和 引用类型 (必须在堆上分配)来做到这一点。 像 int 和 float 这样的类型是值类型, string 和 object 是引用类型。 用户定义的值类型使用 struct 关键字。 用户定义的引用类型使用 class 关键字。 请注意,值类型永远不能保留值null。 在C#中,只能将空值分配给引用类型。 在继续操作时,请记住这一区别。

    Being good performance citizens, we try to avoid heap allocations unless they are necessary. But sometimes we need to convert a value type on the stack into a reference type on the heap. This process is called boxing. Boxing:

    作为良好性能的公民,除非有必要,否则我们将避免堆分配。 但是有时我们需要将堆栈上的值类型转换为堆上的引用类型。 此过程称为 装箱 。 拳击:

    1. Allocates space on the heap

      在堆上分配空间

    2. Informs the garbage collector about the new object

      通知垃圾收集器有关新对象的信息

    3. Copies the data from the value type object into the new reference type object

      将数据从值类型对象复制到新的引用类型对象中

    Ugh, let’s add boxing to our list of things to avoid!

    gh,让我们将拳击添加到要避免的事情清单中!

    那个讨厌的编译器 (That pesky compiler)

    Suppose we are happily writing code, avoiding unnecessary heap allocations and boxing. Maybe we have some trees for our world, and each has a size which scales with its age:

    假设我们愉快地编写代码,避免不必要的堆分配和装箱。 也许我们的世界有几棵树,每棵树的大小都随年龄而变化:

    1

    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    interface HasSize {
       int CalculateSize();
    }
    struct Tree : HasSize {
       private int years;
       public Tree(int age) {
           years = age;
       }
       public int CalculateSize() {
           return years*3;
       }
    }

    1

    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    interface HasSize {
       int CalculateSize ( ) ;
    }
    struct Tree : HasSize {
       private int years ;
       public Tree ( int age ) {
           years = age ;
       }
       public int CalculateSize ( ) {
           return years* 3 ;
       }
    }

    Elsewhere in our code, we have this convenient method to sum up the size of many things (including possibly Tree objects):

    在代码的其他地方,我们有这种方便的方法来汇总许多事物(包括 Tree 对象)的大小:

    1

    2
    3
    4
    5
    6
    7
    8
    public static int TotalSize<T>(params T[] things) where T : HasSize
    {
       var total = 0;
       for (var i = 0; i < things.Length; ++i)
           if (things[i] != null)
               total += things[i].CalculateSize();
       return total;
    }

    1

    2
    3
    4
    5
    6
    7
    8
    public static int TotalSize < T > ( params T [ ] things ) where T : HasSize
    {
       var total = 0 ;
       for ( var i = 0 ; i < things . Length ; ++ i )
           if ( things [ i ] != null )
               total += things [ i ] . CalculateSize ( ) ;
       return total ;
    }

    This looks safe enough, but let’s peer into a little bit of the Intermediate Language (IL) code that the C# compiler generates:

    这看起来足够安全,但让我们看一下C#编译器生成的一些中间语言(IL)代码:

    1

    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    // This is the start of the for loop
    // Load the array
    IL_0009: ldarg.0
    // Load the current index
    IL_000a: ldloc.1
    // Load element at the current index
    IL_000b: ldelem.any !!T
    // What is this box call doing in here?!?
    // (Hint: see the null check in the C# code)
    IL_0010: box !!T
    IL_0015: brfalse IL_002f
    // Set up the arguments for the method and it call
    IL_001a: ldloc.0
    IL_001b: ldarg.0
    IL_001c: ldloc.1
    IL_001d: ldelema !!T
    IL_0022: constrained. !!T
    IL_0028: callvirt instance int32 Unity.IL2CPP.IntegrationTests.Tests.ValueTypeTests.ValueTypeTests/
                                       IHasSize::CalculateSize()
    IL_002f: // Do the next loop iteration...

    1

    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    // This is the start of the for loop
    // Load the array
    IL_0009 : ldarg . 0
    // Load the current index
    IL_000a : ldloc . 1
    // Load element at the current index
    IL_000b : ldelem . any ! ! T
    // What is this box call doing in here?!?
    // (Hint: see the null check in the C# code)
    IL_0010 : box ! ! T
    IL_0015 : brfalse IL_002f
    // Set up the arguments for the method and it call
    IL_001a : ldloc . 0
    IL_001b : ldarg . 0
    IL_001c : ldloc . 1
    IL_001d : ldelema ! ! T
    IL_0022 : constrained . ! ! T
    IL_0028 : callvirt instance int32 Unity . IL2CPP . IntegrationTests . Tests . ValueTypeTests . ValueTypeTests /
                                       IHasSize :: CalculateSize ( )
    IL_002f : // Do the next loop iteration...

    The C# compiler has implemented the if (things[i] != null) check using boxing! If the type T is already a reference type, then the box opcode is pretty cheap – it just returns the existing pointer to the array element. But if type T is a value type (like our Tree type), then that box opcode is very costly. Of course, value types can never be null, so why do we need to implement the check in the first place? And what if we need to compute the size of one hundred Tree objects, or maybe one thousand Tree objects? That unnecessary boxing will quickly become very important.

    C#编译器已 使用装箱 实现了 if(things [i]!= null) 检查! 如果类型 T 已经是引用类型,则 框 操作码非常便宜-它仅返回指向数组元素的现有指针。 但是,如果类型 T 是一个值类型(例如我们的 Tree 类型),那么该 框 操作码将 非常 昂贵。 当然,值类型永远不能为 null ,那么为什么我们首先需要实现检查? 如果我们需要计算一百个 Tree 对象或一千个 Tree 对象 的大小,该 怎么办? 不必要的拳击将很快变得 非常 重要。

    最快的代码是您不执行的任何东西 (The fastest code is anything you don’t execute)

    The C# compiler needs to provide a general implementation that works for any possible type T, so it is stuck with this slower code. But a compiler like IL2CPP can be a bit more aggressive when it generates code that will be executed and when it doesn’t generate the code that won’t!

    C#编译器需要提供适用于任何可能的类型 T 的通用实现 ,因此它会卡在此较慢的代码中。 但是,像IL2CPP这样的编译器在生成将要执行的代码时以及在不生成将不会执行的代码时可能更具攻击性!

    IL2CPP will create an implementation of The TotalSize<T> method specifically for the case where T is a Tree. the IL code above looks like this in generated C++ code:

    IL2CPP将 专门针对 T 是 Tree 的情况 创建 TotalSize <T> 方法 的实现 。 上面的IL代码在生成的C ++代码中看起来像这样:

    1

    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    IL_0009:
    // Load the array
    TreeU5BU5D_t4162282477* L_0 = ___things0;
    // Load the current index
    int32_t L_1 = V_1;
    NullCheck(L_0);
    IL2CPP_ARRAY_BOUNDS_CHECK(L_0, L_1);
    int32_t L_2 = L_1;
    // Load the element at the current index
    Tree_t1533456772  L_3 = (L_0)->GetAt(static_cast<il2cpp_array_size_t>(L_2));
    // Look Ma, no box and no branch!
    // Set up the arguments for the method and it call
    int32_t L_4 = V_0;
    TreeU5BU5D_t4162282477* L_5 = ___things0;
    int32_t L_6 = V_1;
    NullCheck(L_5);
    IL2CPP_ARRAY_BOUNDS_CHECK(L_5, L_6);
    int32_t L_7 = Tree_CalculateSize_m1657788316((Tree_t1533456772 *)(
                    (L_5)->GetAddressAt(static_cast<il2cpp_array_size_t>(L_6))), /*hidden argument*/NULL);
    // Do the next loop iteration...

    1

    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    IL_0009 :
    // Load the array
    TreeU5BU5D_t4162282477* L_0 = ___things0 ;
    // Load the current index
    int32_t L_1 = V_1 ;
    NullCheck ( L_0 ) ;
    IL2CPP_ARRAY_BOUNDS_CHECK ( L_0 , L_1 ) ;
    int32_t L_2 = L_1 ;
    // Load the element at the current index
    Tree _ t1533456772  L_3 = ( L_0 ) -> GetAt ( static_cast < il2cpp_array_size_t > ( L_2 ) ) ;
    // Look Ma, no box and no branch!
    // Set up the arguments for the method and it call
    int32_t L_4 = V_0 ;
    TreeU5BU5D_t4162282477* L_5 = ___things0 ;
    int32_t L_6 = V_1 ;
    NullCheck ( L_5 ) ;
    IL2CPP_ARRAY_BOUNDS_CHECK ( L_5 , L_6 ) ;
    int32_t L_7 = Tree_CalculateSize_m1657788316 ( ( Tree_t1533456772 * ) (
                     ( L_5 ) -> GetAddressAt ( static_cast < il2cpp_array_size_t > ( L_6 ) ) ) , /*hidden argument*/ NULL ) ;
    // Do the next loop iteration...

    IL2CPP recognized that the box opcode is unnecessary for a value type, because we can prove ahead of time that a value type object will never be null. In a tight loop, this removal of an unnecessary allocation and copy of data can have a significant positive impact on performance.

    IL2CPP认识到 对于值类型而言, 框 操作码不是必需的,因为我们可以提前证明值类型对象永远不会为 null 。 在一个紧密的循环中,这种不必要的分配和数据副本的删除会对性能产生重大的积极影响。

    结语 (Wrapping up)

    As with the other micro-optimizations discussed in this series, this one is a common optimization for .NET code generators. All of the scripting backends used by Unity currently perform this optimization for you, so you can get back to writing your code.

    与本系列中讨论的其他微优化一样,这是.NET代码生成器的常见优化。 当前,Unity使用的所有脚本后端均会为您执行此优化,因此您可以重新开始编写代码。

    We hope you have enjoyed this miniseries about micro-optimizations. As we continue to improve the code generators and runtimes used by Unity, we’ll offer more insight into the micro-optimizations that go on behind the scenes.

    我们希望您喜欢这个关于微优化的迷你系列。 随着我们继续改进Unity使用的代码生成器和运行时,我们将提供更多有关幕后微优化的信息。

    翻译自: https://blogs.unity3d.com/2016/08/11/il2cpp-optimizations-avoid-boxing/

    il2cpp

    展开全文
  • il2cpp 这是IL2CPP Internals系列的第八篇也是最后一篇文章。 在本文中,我将与以前的内容有所不同,而不是讨论IL2CPP在编译时或运行时如何工作的某些方面。 相反,我们将简要概述如何开发和测试IL2CPP。 (This is ...

    il2cpp

    This is the eighth and final post in the IL2CPP Internals series. In this post I’ll deviate a bit from the content of previous posts, and not discuss some aspect of how IL2CPP works at compile time or run time. Instead, we’ll take a look at a brief overview of how we develop and test IL2CPP.

    这是IL2CPP Internals系列的第八篇也是最后一篇文章。 在本文中,我将与以前的内容有所不同,而不是讨论IL2CPP在编译时或运行时如何工作的某些方面。 相反,我们将简要概述如何开发和测试IL2CPP。

    测试优先开发 (Test-first development)

    The IL2CPP team has a strong test-first development mentality. Much of the code for IL2CPP is written using the practice of Test Driven Development (TDD), and very few pull requests are merged to the IL2CPP code without significant test coverage.

    IL2CPP团队具有强烈的测试优先开发心态。 IL2CPP的许多代码都是使用“ 测试驱动开发” (TDD)的做法编写的,并且很少有拉取请求被合并到IL2CPP代码中,而没有相当大的测试范围。

    Since IL2CPP has a finite (although rather large) set of inputs – the ECMA 335 spec- the process of developing it fits nicely with TDD concepts. Most of tests are written before production code, and these tests always need to fail in an expected way before the code to make them pass is written.

    由于IL2CPP具有有限的(虽然相当大)输入集-ECMA 335规范-开发它的过程非常适合TDD概念。 大多数测试是在生产代码之前编写的,而这些测试总是需要以预期的方式失败,然后才能编写使它们通过的代码。

    This process helps to drive the design of IL2CPP, but it also provides the development team with a large bank of tests which run rather quickly and exercise nearly all of the existing behavior in IL2CPP. As a development team, this test suite provides two important benefits.

    此过程有助于推动IL2CPP的设计,但它也为开发团队提供了大量测试,这些测试运行得相当快,几乎可以执行IL2CPP中的所有现有行为。 作为开发团队,此测试套件具有两个重要的好处。

      测试统计 (Testing statistics)

      The various types of tests that we run against the IL2CPP code base break down into a few different levels. Here are the number of tests we current have a each level (I’ll discuss what each type of test actually is below).

      针对IL2CPP代码库运行的各种类型的测试分为几个不同的级别。 这是我们当前每个级别具有的测试数量(我将在下面讨论每种测试的实际含义)。

      • Unit tests

        单元测试

        • Integration tests

          整合测试

          If all of these tests are green, then we feel confident that we can ship IL2CPP at that moment. We maintain one main development branch for IL2CPP, which always tracks the leading edge branch for development in Unity as a whole. The tests are always green on this main development branch. When they break (which does happen once in a while), someone usually fixes them within a few minutes.

          如果所有这些测试都是绿色的,那么我们有信心在那时可以发货IL2CPP。 我们为IL2CPP维护了一个主要的开发分支,该分支始终跟踪整个Unity开发的前沿分支。 在这个主要的开发分支上,测试始终是绿色的。 当它们断裂时(偶尔会发生),通常有人会在几分钟之内修复它们。

          Since developers on our team are forking this main branch for personal development often, it needs to be green at all times. The build and test status for both the main development branch and personal branches are maintained on Katana, Unity’s internal build management system.

          由于我们团队中的开发人员经常将这一主要分支用于个人开发,因此它需要始终保持绿色。 主开发部门和个人部门的构建和测试状态均由Unity的内部构建管理系统Katana维护。

          We use NUnit to run all of these tests and the drive NUnit in one of three different ways

          我们使用NUnit以三种不同方式之一运行所有这些测试并驱动NUnit

          Types of tests

          测试类型

          I mentioned four different types of tests above without much explanation. Each of these types of tests serves a different purpose, and they all work together to help keep IL2CPP development moving forward.

          我上面提到了四种不同类型的测试,但没有太多解释。 每种类型的测试都有不同的用途,并且它们都可以共同帮助保持IL2CPP的发展。

          The unit tests verify the behavior of a small bit of code, typically a method. They set up a situation, execute the code under test, and finally assert some expected behavior.

          单元测试可以验证少量代码的行为,通常是一种方法。 他们设置了一个情况,执行了测试中的代码,最后声明了一些预期的行为。

          The integration tests for IL2CPP actually run the il2cpp.exe utility on an assembly, compile the generated C++ code to an executable, then run the executable. Since we have a nice reference for IL2CPP behavior (the existing version of Mono used in Unity), these integration tests also run the same assembly with Mono (and .Net, on Windows). Our test runner then compares the results of the two (or three) runs dumped to standard output and reports any differences. So the IL2CPP integration tests don’t have explicit expected values or assertions listed in the test code like the unit tests do.

          IL2CPP的集成测试实际上是在程序集上运行il2cpp.exe实用程序,将生成的C ++代码编译为可执行文件,然后运行该可执行文件。 由于我们对IL2CPP行为(Unity中使用的Mono的现有版本)有很好的参考,因此这些集成测试还使用Mono(和Windows中的.Net)运行相同的程序集。 然后,我们的测试运行器比较转储到标准输出中的两个(或三个)运行的结果,并报告任何差异。 因此,IL2CPP集成测试没有像单元测试那样在测试代码中列出明确的期望值或断言。

          C# unit tests

          C#单元测试

          These tests are the fastest, and lowest level tests that we write. They are used to verify the behavior of many parts of il2cpp.exe, the AOT compiler utility for IL2CPP. Since il2cpp.exe is written entirely in C#, we can use fast C# unit tests to get good turn-around time for changes. All of the C# unit tests complete in a few seconds on a nice development machine.

          这些测试是我们编写的最快,最低级别的测试。 它们用于验证il2cpp.exe(IL2CPP的AOT编译器实用程序)的许多部分的行为。 由于il2cpp.exe完全用C#编写,因此我们可以使用快速的C#单元测试来获得良好的更改周转时间。 所有C#单元测试都在一台不错的开发机器上在几秒钟内完成。

          C++ unit tests

          C ++单元测试

          The vast majority of the runtime code for IL2CPP (called libil2cpp) is written in C++. For parts of that code which are not easily accessible from a public API, we use C++ unit tests. We have relatively few of these tests, as most of the behavior of code in libil2cpp can be exercised via our larger integration test suite. These tests to require more time than you might expect for unit tests to run, as they need to run il2cpp.exe itself to set up their fixture data.

          IL2CPP的绝大多数运行时代码(称为libil2cpp)都是用C ++编写的。 对于无法从公共API轻松访问的部分代码,我们使用C ++单元测试。 这些测试相对较少,因为libil2cpp中的大多数代码行为都可以通过更大的集成测试套件来实现。 这些测试所需的时间比运行单元测试所需的时间长,因为它们需要运行il2cpp.exe本身来设置其夹具数据。

          C# integration tests

          C#集成测试

          The largest and most comprehensive test suite for IL2CPP is the C# integration test suite. These tests a divided into smaller segments, focusing on tests that verify behavior of icalls, code generation, p/invoke, and general behavior. Most of the tests in this suite are rather short, only about 5 – 10 lines long. The entire suite runs in less than one minute on most machines, but we can run it with various IL2CPP options related to things like stripping and code generation.

          C2集成测试套件是针对IL2CPP的最大,最全面的测试套件。 这些测试分为较小的部分,重点是验证icalls行为,代码生成,p / invoke和常规行为的测试。 该套件中的大多数测试都相当短,只有大约5 – 10行长。 整个套件在大多数计算机上的运行时间不到一分钟,但是我们可以使用与剥离和代码生成等相关的各种IL2CPP选项来运行它。

          IL integration tests

          IL整合测试

          These tests are similar in toolchain to the C# integration tests. However, instead of writing the test code in C#, we use the ILGenerator class to directly create an assembly. Although these tests can take a bit more time to write than C# tests, they offer increased flexibility. Often we run into problems with IL code that is invalid or not generated by our current Mono C# compiler. In these cases, we can often write a good test case with IL code. The tests are also beneficial for comprehensive testing of opcodes like conv.i (and similar opcodes in its family) which have clear behavior with many slight variations. All of the IL tests complete end to end in less than one minute.

          这些测试在工具链中与C#集成测试相似。 但是,不是使用C#编写测试代码,而是使用ILGenerator类直接创建一个程序集。 尽管这些测试可能比C#测试花费更多时间,但是它们提供了更大的灵活性。 通常,我们当前的Mono C#编译器会生成无效或未生成的IL代码问题。 在这些情况下,我们通常可以使用IL代码编写一个好的测试用例。 这些测试还有助于对conv.i (及其系列中的类似操作码)之类的操作码进行全面测试,这些操作码具有明显的行为,但有许多细微变化。 所有的IL测试都在一分钟之内完成首尾相接。

          We run all of these tests through many variations and options on Katana. From a clean pull of the source code to completed test runs, we see about 20-30 minutes of runtime depending on the load on the build farm.

          我们通过Katana上的许多变体和选项来运行所有这些测试。 从源代码的干净提取到完成的测试运行,根据构建场的负载,我们看到大约20-30分钟的运行时间。

          为什么要进行这么多的集成测试? (Why so many integration tests?)

          Based on these descriptions, it might seem like our test pyramid for IL2CPP is upside down. And indeed, the end-to-end integration tests (near the top of the pyramid) make up most of our test coverage.

          基于这些描述,似乎我们的IL2CPP 测试金字塔倒挂了。 实际上,端到端集成测试(在金字塔顶部附近)构成了我们测试的大部分内容。

          Following TDD practice with test times more than a few seconds can be difficult as well. We work to mitigate this by allowing individual segments of the integration test suites to run, and by doing incremental building of the C++ code generated in the test suites (this is how we are proving out some incremental building possibilities for Unity projects with IL2CPP, so stay tuned). Then the turn-around time for an individual test is reasonable (although still not as fast as we would like).

          按照TDD练习,测试时间超过几秒钟也很困难。 我们通过允许运行集成测试套件的各个部分,以及通过增量构建测试套件中生成的C ++代码的方式来减轻这种情况(这就是我们为带有IL2CPP的Unity项目提供一些增量构建可能性的方式,因此敬请关注)。 这样,单个测试的周转时间是合理的(尽管仍然没有我们想要的那么快)。

          This heavy use of integration tests was a conscious decision though. Much of the code in IL2CPP looks different than it used to, even at our initial public releases in January of 2015. We have learned plenty and changed many of the implementation details in the IL2CPP code base since its inception, but we still have many of the original tests written years ago. After trying out tests at a number of different levels (including even validating the content of the generated C++ source code), we decided that these integration tests give us the best runtime to test stability ratio. Seldom, if ever, do we need to modify one of the existing integration tests when something changes in the IL2CPP code. This fact gives us tremendous confidence that a code change which causes a test to fail is really a problem. It also let’s us refactor and improve the IL2CPP code as much as we need to without fear.

          但是,大量使用集成测试是一个有意识的决定。 即使在2015年1月的首次公开发布中,IL2CPP中的许多代码看起来也与以前不同。自IL2CPP代码库问世以来,我们已经学到了很多东西,并更改了许多实现细节,但是我们仍然有很多几年前写的原始测试。 在尝试了许多不同级别的测试(甚至包括验证生成的C ++源代码的内容)之后,我们认为这些集成测试为我们提供了最佳的运行时测试稳定性比率。 很少(如果有的话),当IL2CPP代码发生变化时,我们是否需要修改现有的集成测试之一。 这一事实使我们充满信心,认为导致测试失败的代码更改确实是一个问题。 这也使我们可以重构和改进IL2CPP代码,而无需担心。

          更大的测试 (Even larger tests)

          Outside of IL2CPP itself, the IL2CPP code fits into the much larger Unity testing ecosystem. For each platform we ship supporting IL2CPP, we execute the Unity player runtime tests. These tests build up a single Unity project with more than 1000 scenes, then execute each scene and validate expected behavior via assertions. We usually don’t add new tests to this suite for IL2CPP changes (those tests usually end up being at a lower level). This suite serves as a check against regressions that we might introduce with IL2CPP on a given platform. This suite also allows us to test the code used in integration IL2CPP into the Unity build toolchain, which again varies for each platform. A typical runtime test suite completes on about 60-90 minutes, although we often execute individual tests locally much faster.

          在IL2CPP本身之外,IL2CPP代码适合更大的Unity测试生态系统。 对于我们支持IL2CPP的每个平台,我们都执行Unity Player运行时测试。 这些测试将构建一个包含1000多个场景的Unity项目,然后执行每个场景并通过断言验证预期的行为。 我们通常不会在此套件中为IL2CPP更改添加新测试(这些测试通常最终处于较低级别)。 该套件用作对我们可能在给定平台上使用IL2CPP引入的回归的检验。 该套件还使我们能够测试将IL2CPP集成到Unity构建工具链中所使用的代码,该代码对于每个平台也有所不同。 典型的运行时测试套件大约需要60-90分钟才能完成,尽管我们通常会在本地更快地执行单个测试。

          The largest and slowest tests we use for IL2CPP are Unity editor integration tests. Each of these tests actually runs a different instance of the Unity editor. Most of the IL2CPP editor integration tests focus on building a running a project, usually with various editor build settings. We use these tests to verify things like complex editor integration, error message reporting, and project build size (among many others). Depending on the platform, integration test suites run in a few hours, and usually are executed at least nightly, if not more often.

          我们用于IL2CPP的最大,最慢的测试是Unity编辑器集成测试。 这些测试中的每一个实际上都运行Unity编辑器的不同实例。 大多数IL2CPP编辑器集成测试通常着重于使用各种编辑器构建设置来构建运行项目。 我们使用这些测试来验证诸如复杂的编辑器集成,错误消息报告和项目构建大小(等等)之类的事情。 根据平台的不同,集成测试套件会在几个小时内运行,并且通常至少每晚执行一次(如果不是更频繁的话)。

          这些测试有什么影响? (What is the impact of these tests?)

          At Unity, one of our guiding principles is “solve hard problems”. I like to think about the difficulty of problems in terms of failure. The more difficult a problem is to solve, the more failures I need accomplish before I can find the solution.

          在Unity,我们的指导原则之一是“解决难题”。 我喜欢从失败的角度考虑问题的难度。 解决问题的难度越大,找到解决方案之前我需要完成的故障越多。

          Creating a new highly-performant, highly-portable AOT compiler and virtual machine to use as a scripting backend in Unity is a difficult problem. Needless to say, we’ve accomplished thousands of failures along the way. There are more problems to solve, and so more failures to come. But by capturing the useful information from almost all of those failures in a comprehensive and fast test suite, we can iterate very quickly.

          创建一个新的高性能,高度可移植的AOT编译器和虚拟机以用作Unity中的脚本后端是一个难题。 不用说,我们已经完成了成千上万的失败。 还有更多的问题需要解决,因此会有更多的失败。 但是,通过在全面,快速的测试套件中捕获几乎所有此类故障的有用信息,我们可以非常快速地进行迭代。

          For the IL2CPP developers, our test suite is not so much a means to verify bug-free code (although it does catch bugs), or to help port IL2CPP to multiple platforms (it does that too), but rather, it is a tool we can use to fail fast and solve hard problems so our users can focus on creating beautiful things.

          对于IL2CPP开发人员而言,我们的测试套件不是验证无错误代码(尽管它确实捕获到错误)或帮助将IL2CPP移植到多个平台(也可以这样做)的一种手段,而是一种工具我们可以用来快速失败并解决难题,因此我们的用户可以专注于创造美丽的事物。

          结论 (Conclusion)

          We hope that you have enjoyed the IL2CPP Internals series of posts. We’re happy to share implementation details and provide debugging and performance hints when we can. Let us know if you want to hear more about other topics related to the design and implementation of IL2CPP.

          我们希望您喜欢IL2CPP Internals系列文章。 我们很乐意分享实施细节,并在可能时提供调试和性能提示。 如果您想进一步了解与IL2CPP的设计和实现有关的其他主题,请告诉我们。

          翻译自: https://blogs.unity3d.com/2015/07/20/il2cpp-internals-testing-frameworks/

          il2cpp

          展开全文
        • il2cpp 这是IL2CPP Internals系列中的第五篇 。 在上一篇文章中,我们研究了如何在为IL2CPP脚本编写后端生成的C ++代码中调用方法。 在本文中,我们将探讨如何实现它们。 具体来说,我们将尝试更好地理解IL2CPP生成...

          il2cpp

          This is the fifth post in the IL2CPP Internals series. In the last post, we looked at how methods are called in the C++ code generated for the IL2CPP scripting backend. In this post, we will explore how they are implemented. Specifically, we will try to better understand one of the most important features of code generated with IL2CPP – generic sharing. Generic sharing allows many generic methods to share one common implementation. This leads to significant decreases in executable size for the IL2CPP scripting backend.

          这是IL2CPP Internals系列中的第五 。 在上一篇文章中,我们研究了如何在为IL2CPP脚本编写后端生成的C ++代码中调用方法。 在本文中,我们将探讨如何实现它们。 具体来说,我们将尝试更好地理解IL2CPP生成的代码的最重要功能之一-通用共享。 通用共享允许许多通用方法共享一个通用实现。 这导致IL2CPP脚本后端的可执行文件大小显着减小。

          Note that generic sharing is not a new idea, both Mono and .Net runtimes use generic sharing as well. Initially, IL2CPP did not perform generic sharing. Recent improvements have made it even more robust and beneficial. Since il2cpp.exe generates C++ code, we can see where the method implementations are shared.

          请注意,通用共享不是一个新想法,Mono和.Net运行时也都使用通用共享。 最初,IL2CPP不执行通用共享。 最近的改进使其更加强大和有益。 由于il2cpp.exe生成C ++代码,因此我们可以看到共享方法实现的位置。

          We will explore how generic method implementations are shared (or not) for reference types and value types.  We will also investigate how generic parameter constraints affect generic sharing.

          我们将探讨如何为引用类型和值类型共享(或不共享)通用方法实现。 我们还将研究通用参数约束如何影响通用共享。

          Keep in mind that everything discussed in this series are implementation details. The topics and code discussed here are likely to change in the future. We like to expose and discuss details like this when it is possible though!

          请记住,本系列中讨论的所有内容都是实现细节。 此处讨论的主题和代码将来可能会更改。 我们希望尽可能地公开和讨论这样的细节!

          What is generic sharing?

          什么是通用共享?

          Imagine you are writing the implementation for the List<T> class in C#. Would that implementation depend on the type T is? Could you use the same implementation of the Add method for List<string> and List<object>? How about List<DateTime>?

          想象一下,您正在用C#编写List<T>类的实现。 该实现取决于类型T吗? 您可以对List<string>List<object>使用Add方法的相同实现吗? List<DateTime>怎么样?

          In fact, the power of generics is just that these C# implementations can be shared, and the generic class List<T> will work for any T. But what happens when List is translated from C# to something executable, like assembly code (as Mono does) or C++ code (as IL2CPP does)? Can we still share the implementation of the Add method?

          实际上,泛型的力量仅仅是可以共享这些C#实现,而泛型类List<T>将可用于任何T 但是,如果将List从C#转换为可执行文件,例如汇编代码(如Mono)或C ++代码(如IL2CPP),会发生什么? 我们是否仍可以共享Add方法的实现?

          Yes, we can share it most of the time. As we’ll discover in this post, the ability to share the implementation of a generic method depends almost entirely on the size of that type T. If T is any reference type (like string or object), then it will always be the size of a pointer. If T is a value type (like int or DateTime), its size may vary, and things get a bit more complex. The more method implementations which can be shared, the smaller the resulting executable code is.

          是的,我们大多数时间都可以共享。 正如我们将在这篇文章中发现的,共享通用方法的实现的能力几乎完全取决于类型T的大小。 如果T是任何引用类型(例如stringobject ),则它将始终是指针的大小。 如果T是一个值类型(如intDateTime ),则其大小可能会有所不同,并且事情会变得更加复杂。 可以共享的方法实现越多,生成的可执行代码就越小。

          Mark Probst, the developer who implemented generic sharing Mono, has an excellent series of posts on how Mono performs generic sharing. We won’t go into that much depth about generic sharing here. Instead, we will see how and when IL2CPP performs generic sharing. Hopefully this information will help you better analyze and understand the executable size of your project.

          实现了通用共享Mono的开发人员Mark Probst撰写了一系列精彩的文章 ,介绍了Mono如何执行通用共享。 我们不会在这里深入探讨泛型共享。 相反,我们将看到IL2CPP如何以及何时执行通用共享。 希望这些信息将帮助您更好地分析和理解项目的可执行文件大小。

          What is shared by IL2CPP?

          IL2CPP有什么共同点?

          Currently, IL2CPP shares generic method implementations for a generic type SomeGenericType<T> when T is:

          当前,当T SomeGenericType<T> ,IL2CPP共享通用类型SomeGenericType<T>通用方法实现:

          • Any reference type (e.g. string, object, or any user defined class)

            任何引用类型(例如stringobject或任何用户定义的类)

          IL2CPP does not share generic method implementations when T is a value type because the size of each value type will differ (based on the size of its fields).

          T为值类型时,IL2CPP不共享通用方法实现,因为每种值类型的大小将有所不同(基于其字段的大小)。

          Practically, this means that adding a new usage of SomeGenericType<T>, where T is a reference type will have a minimal impact on the executable size. However, if T is a value type, the executable size will be impacted. This behavior is the same for both the Mono and IL2CPP scripting backends. If you want to know more, read on, it’s time to dig into some implementation details!

          实际上,这意味着添加SomeGenericType<T>的新用法(其中T是引用类型)对可执行文件大小的影响最小。 但是,如果T是值类型,则可执行文件的大小将受到影响。 Mono和IL2CPP脚本后端的行为相同。 如果您想了解更多信息,请继续阅读,是时候深入了解一些实现细节了!

          The setup

          设置

          I’ll be using Unity 5.0.2p1 on Windows, and building for the WebGL platform. I’ve enabled the “Development Player” option in the build settings, and the “Enable Exceptions” option is set to a value of “None”. The script code for this post starts with a driver method to create instances of the generic types we will investigate:

          我将在Windows上使用Unity 5.0.2p1,并为WebGL平台构建。 我已经在构建设置中启用了“ Development Player”选项,并且“ Enable Exceptions”选项设置为“ None”。 这篇文章的脚本代码从驱动程序方法开始,以创建我们将研究的泛型类型的实例:

          1

          2
          3
          4
          5
          6
          public void DemonstrateGenericSharing() {
          var usesAString = new GenericType<string>();
          var usesAClass = new GenericType<AnyClass>();
          var usesAValueType = new GenericType<DateTime>();
          var interfaceConstrainedType = new InterfaceConstrainedGenericType<ExperimentWithInterface>();
          }

          1

          2
          3
          4
          5
          6
          public void DemonstrateGenericSharing ( ) {
          var usesAString = new GenericType < string > ( ) ;
          var usesAClass = new GenericType < AnyClass > ( ) ;
          var usesAValueType = new GenericType < DateTime > ( ) ;
          var interfaceConstrainedType = new InterfaceConstrainedGenericType < ExperimentWithInterface > ( ) ;
          }

          Next, we define the types used in this method:

          接下来,我们定义此方法中使用的类型:

          1

          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          12
          13
          14
          15
          16
          17
          18
          19
          20
          21
          22
          23
          24
          25
          26
          27
          28
          29
          class GenericType<T> {
          public T UsesGenericParameter(T value) {
          return value;
          }
          public void DoesNotUseGenericParameter() {}
          public U UsesDifferentGenericParameter<U>(U value) {
          return value;
          }
          }
          class AnyClass {}
          interface AnswerFinderInterface {
          int ComputeAnswer();
          }
          class ExperimentWithInterface : AnswerFinderInterface {
          public int ComputeAnswer() {
          return 42;
          }
          }
          class InterfaceConstrainedGenericType<T> where T : AnswerFinderInterface {
          public int FindTheAnswer(T experiment) {
          return experiment.ComputeAnswer();
          }
          }

          1

          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          12
          13
          14
          15
          16
          17
          18
          19
          20
          21
          22
          23
          24
          25
          26
          27
          28
          29
          class GenericType < T > {
          public T UsesGenericParameter ( T value ) {
          return value ;
          }
          public void DoesNotUseGenericParameter ( ) { }
          public U UsesDifferentGenericParameter < U > ( U value ) {
          return value ;
          }
          }
          class AnyClass { }
          interface AnswerFinderInterface {
          int ComputeAnswer ( ) ;
          }
          class ExperimentWithInterface : AnswerFinderInterface {
          public int ComputeAnswer ( ) {
          return 42 ;
          }
          }
          class InterfaceConstrainedGenericType < T > where T : AnswerFinderInterface {
          public int FindTheAnswer ( T experiment ) {
          return experiment . ComputeAnswer ( ) ;
          }
          }

          And all of code is nested in a class named HelloWorld derived from MonoBehaviour.

          并且所有代码都嵌套在派生自MonoBehaviour名为HelloWorld的类中。

          If you view the command line for il2cpp.exe, note that it does not contain the --enable-generic-sharing option, as described in the first post in this series. However, generic sharing is still occurring. It is no longer optional, and happens in all cases now.

          如果您查看il2cpp.exe的命令行,请注意,它不包含--enable-generic-sharing option ,如本系列第一篇文章中所述。 但是,通用共享仍在发生。 它不再是可选的,并且现在在所有情况下都会发生。

          Generic sharing for reference types

          参考类型的通用共享

          We’ll start by looking at the most often occurring generic sharing case: reference types. Since all reference types in managed code derive from System.Object, all reference types in the generated C++ code derive from the Object_t type. All reference types can then be represented in C++ code using the type Object_t* as a placeholder. We’ll see why this is important in a moment.

          我们将从研究最常见的通用共享案例开始:引用类型。 由于托管代码中的所有引用类型均源自System.Object ,因此生成的C ++代码中的所有引用类型均源自Object_t类型。 然后,所有引用类型都可以使用Object_t*类型作为占位符以C ++代码表示。 我们稍后将了解为什么这很重要。

          Let’s search for the generated version of the DemonstrateGenericSharing method. In my project it is named HelloWorld_DemonstrateGenericSharing_m4. We’re looking for the method definitions for the four methods in the GenericType class. Using Ctags, we can jump to the method declaration for the GenericType<string> constructor, GenericType_1__ctor_m8. Note that this method declaration is actually a #define statement, mapping the method to another method, GenericType_1__ctor_m10447_gshared.

          让我们搜索DemonstrateGenericSharing方法的生成版本。 在我的项目中,它名为HelloWorld_DemonstrateGenericSharing_m4 。 我们正在寻找GenericType类中四个方法的方法定义。 使用Ctags ,我们可以跳转到GenericType<string>构造函数GenericType_1__ctor_m8的方法声明。 请注意,此方法声明实际上是#define语句,将方法映射到另一个方法GenericType_1__ctor_m10447_gshared

          Let’s jump back, back and then find the method declarations for the GenericType<AnyClass> type. If we jump to the declaration of the constructor, GenericType_1__ctor_m9, we can see that it is also a #define statement, mapped to the same function, GenericType_1__ctor_m10447_gshared!

          让我们来回跳,然后找到GenericType<AnyClass>类型的方法声明。 如果我们跳到构造函数GenericType_1__ctor_m9的声明,我们可以看到它也是#define语句,映射到相同的函数GenericType_1__ctor_m10447_gshared

          If we jump to the definition of GenericType_1__ctor_m10447_gshared, we can see from the code comment on the method definition that this method corresponds to the managed method name HelloWorld/GenericType`1<System.Object>::.ctor(). This is the constructor for the GenericType<object> type. This type is called the fully shared type, meaning that given a type GenericType<T>, for any T that is a reference type, the implementation of all methods will use this version, where T is object.

          如果我们跳到GenericType_1__ctor_m10447_gshared的定义,可以从方法定义的代码注释中看到,该方法对应于托管方法名称HelloWorld/GenericType`1<System.Object>::.ctor() 。 这是GenericType<object>类型的构造函数。 此类型称为完全共享类型,这意味着给定类型GenericType<T> ,对于任何作为引用类型的T ,所有方法的实现都将使用此版本,其中Tobject

          Look just below the constructor in the generated code, and you should see the C++ code for the UsesGenericParameter method:

          在生成的代码中的构造函数下方查看,您应该看到UsesGenericParameter方法的C ++代码:

          1

          2
          3
          4
          5
          6
          7
          extern "C" Object_t * GenericType_1_UsesGenericParameter_m10449_gshared (GenericType_1_t2159 * __this, Object_t * ___value, MethodInfo* method)
          {
          {
          Object_t * L_0 = ___value;
          return L_0;
          }
          }

          1

          2
          3
          4
          5
          6
          7
          extern "C" Object_t * GenericType_1_UsesGenericParameter_m10449_gshared ( GenericType_1_t2159 * __this , Object_t * ___value , MethodInfo* method )
          {
          {
          Object_t * L_0 = ___value ;
          return L_0 ;
          }
          }

          In both places where the generic parameter T is used (the return type and the type of the single managed argument), the generated code uses the Object_t* type. Since all reference types can be represented in the generated code by Object_t*, we can call this single method implementation for any T that is a reference type.

          在使用通用参数T两个地方(返回类型和单个托管参数的类型),生成的代码都使用Object_t*类型。 由于所有引用类型都可以由Object_t*表示在生成的代码中, Object_t*我们可以为作为引用类型的任何T调用此单一方法实现。

          In the second blog post in this series (about generated code), we mentioned that all method definitions are free functions in C++. The il2cpp.exe utility does not generate overridden methods in C# using C++ inheritance. However, il2cpp.exe does use C++ inheritance for types. If we search the generated code for the string “AnyClass_t” we can find the C++ representation of the C# type AnyClass:

          在本系列的第二篇博客文章中(关于生成的代码),我们提到了所有方法定义都是C ++中的自由函数。 il2cpp.exe实用程序不会使用C ++继承在C#中生成重写的方法。 但是,il2cpp.exe确实对类型使用C ++继承。 如果我们在生成的代码中搜索字符串“ AnyClass_t”,则可以找到C#类型AnyClass的C ++表示形式:

          1

          2
          3
          struct  AnyClass_t1  : public Object_t
          {
          };

          1

          2
          3
          struct  AnyClass _ t1  : public Object_t
          {
          } ;

          Since AnyClass_t1 derives from Object_t, we can pass a pointer to AnyClass_t1 as the argument to the GenericType_1_UsesGenericParameter_m10449_gshared function without problems.

          由于AnyClass_t1AnyClass_t1派生的, Object_t我们可以Object_t地将指向AnyClass_t1的指针作为GenericType_1_UsesGenericParameter_m10449_gshared函数的参数传递。

          What about the return value though? We can’t return a pointer to a base class where a pointer to a derived class is expected, right? Take a look at the declaration of the GenericType<AnyClass>::UsesGenericParameter method:

          那返回值呢? 我们不能返回一个指向派生类的指针的基类的指针,对吗? 看一下GenericType<AnyClass>::UsesGenericParameter方法的声明:

          1

          #define GenericType_1_UsesGenericParameter_m10452(__this, ___value, method) (( AnyClass_t1 * (*) (GenericType_1_t6 *, AnyClass_t1 *, MethodInfo*))GenericType_1_UsesGenericParameter_m10449_gshared)(__this, ___value, method)

          1

          #define GenericType_1_UsesGenericParameter_m10452(__this, ___value, method) (( AnyClass_t1 * (*) (GenericType_1_t6 *, AnyClass_t1 *, MethodInfo*))GenericType_1_UsesGenericParameter_m10449_gshared)(__this, ___value, method)

          The generated code is actually casting the return value (type Object_t*) to the derived type AnyClass_t1*. So here IL2CPP is lying to the C++ compiler to avoid the C++ type system. Since the C# compiler has already enforced that no code in UsesGenericParameter does anything unreasonable with type T, then IL2CPP is safe to lie to the C++ compiler here.

          生成的代码实际上将返回值(类型Object_t* )转换为派生类型AnyClass_t1* 。 所以在这里,IL2CPP向C ++编译器撒谎,以避免使用C ++类型的系统。 由于C#编译器已经强制执行UsesGenericParameter中的任何代码都UsesGenericParameterT类型执行任何不合理的操作,因此IL2CPP可以安全地躺在这里的C ++编译器中。

          Generic sharing with constraints

          有约束的通用共享

          Suppose that we want to allow some methods to be called on an object of type T? Won’t the use of Object_t* prevent that, since we don’t have many methods on System.Object? Yes, this is correct. But we first need to express this idea to the C# compiler using generic constraints.

          假设我们希望允许在T类型的对象上调用某些方法? 因为我们在System.Object上没有很多方法,所以使用Object_t*不会阻止这种情况吗? 是的,这是正确的。 但是我们首先需要使用通用约束将这种想法表达给C#编译器。

          Take a look again in the script code for this post at the type named InterfaceConstrainedGenericType. This generic type uses a where clause to require that it type T be derived from a given interface, AnswerFinderInterface. This allows the ComputeAnswer method to be called. Recall from the previous blog post about method invocation that calls on interface methods require a lookup in a vtable structure. Since the FindTheAnswer method will make a direct function call on the constrained instance of type T, then the C++ code can still use the fully shared method implementation, with the type T represented by Object_t*.

          再次查看此帖子的脚本代码中名为InterfaceConstrainedGenericType的类型。 此泛型类型使用where子句,要求其类型T从给定接口AnswerFinderInterface 。 这允许ComputeAnswer方法被调用。 回想一下以前有关方法调用的博客文章,该方法调用接口方法需要在vtable结构中进行查找。 由于FindTheAnswer方法将对类型T的受约束实例进行直接函数调用,因此C ++代码仍可以使用对象类型TObject_t*表示的完全共享方法实现。

          If we start at the implementation of the HelloWorld_DemonstrateGenericSharing_m4 function, then jump to the definition of the InterfaceConstrainedGenericType_1__ctor_m11 function, we can see that this method is again a #define, mapping to the InterfaceConstrainedGenericType_1__ctor_m10456_gshared function. If we look just below that function for the implementation of the InterfaceConstrainedGenericType_1_FindTheAnswer_m10458_gshared function, we can see that indeed, this is the fully shared version of the function, taking an Object_t* argument. It calls the InterfaceFuncInvoker0::Invoke function to actually make the call to the managed ComputeAnswer method.

          如果我们从HelloWorld_DemonstrateGenericSharing_m4函数的实现开始,然后跳转到InterfaceConstrainedGenericType_1__ctor_m11函数的定义,我们可以看到此方法再次是#define ,映射到InterfaceConstrainedGenericType_1__ctor_m10456_gshared函数。 如果我们仅在该函数的下方查看InterfaceConstrainedGenericType_1_FindTheAnswer_m10458_gshared函数的实现,则可以看到,实际上,这是该函数的完全共享版本,带有Object_t*参数。 它调用InterfaceFuncInvoker0::Invoke函数以实际调用托管的ComputeAnswer方法。

          1

          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          12
          13
          extern "C" int32_t InterfaceConstrainedGenericType_1_FindTheAnswer_m10458_gshared (InterfaceConstrainedGenericType_1_t2160 * __this, Object_t * ___experiment, MethodInfo* method)
          {
          static bool s_Il2CppMethodIntialized;
          if (!s_Il2CppMethodIntialized)
          {
          AnswerFinderInterface_t11_il2cpp_TypeInfo_var = il2cpp_codegen_class_from_type(&AnswerFinderInterface_t11_0_0_0);
          s_Il2CppMethodIntialized = true;
          }
          {
          int32_t L_0 = (int32_t)InterfaceFuncInvoker0<int32_t>::Invoke(0 /* System.Int32 HelloWorld/AnswerFinderInterface::ComputeAnswer() */, AnswerFinderInterface_t11_il2cpp_TypeInfo_var, (Object_t *)(*(&amp;amp;___experiment)));
          return L_0;
          }
          }

          1

          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          12
          13
          extern "C" int32_t InterfaceConstrainedGenericType_1_FindTheAnswer_m10458_gshared ( InterfaceConstrainedGenericType_1_t2160 * __this , Object_t * ___experiment , MethodInfo* method )
          {
          static bool s_Il2CppMethodIntialized ;
          if ( ! s_Il2CppMethodIntialized )
          {
          AnswerFinderInterface_t11_il2cpp_TypeInfo_var = il2cpp_codegen_class_from_type ( & AnswerFinderInterface_t11_0_0_0 ) ;
          s_Il2CppMethodIntialized = true ;
          }
          {
          int32_t L_0 = ( int32_t ) InterfaceFuncInvoker0 < int32_t > :: Invoke ( 0 /* System.Int32 HelloWorld/AnswerFinderInterface::ComputeAnswer() */ , AnswerFinderInterface_t11_il2cpp_TypeInfo_var , ( Object_t * ) ( * ( & amp ; amp ; ___experiment ) ) ) ;
          return L_0 ;
          }
          }

          This all hangs together in the generated C++ code code because IL2CPP treats all managed interfaces like System.Object. This is a useful rule of thumb to help understand the code generated by il2cpp.exe in other cases as well.

          由于IL2CPP将所有托管接口(如System.Object都视为对待,因此所有这些都挂在生成的C ++代码中。 在其他情况下,这也是有用的经验法则,可帮助您理解il2cpp.exe生成的代码。

          Constraints with a base class

          具有基类的约束

          In addition to interface constraints, C# allows constraints to be a base class. IL2CPP does not treat all base classes like System.Object, so how does generic sharing work for base class constraints?

          除了接口约束,C#还允许约束成为基类。 IL2CPP不会像System.Object那样对待所有基类,那么泛型共享如何对基类约束起作用?

          Since base classes are always reference types, IL2CPP uses the fully shared version of the generic methods for these types. Any code which needs to use a field or call a method on the constrained type is performs a cast in C++ to the proper type. Again, here we rely on the C# compiler to correctly enforce the generic constraint, and we lie to the C++ compiler about the type.

          由于基类始终是引用类型,因此IL2CPP对这些类型使用通用方法的完全共享版本。 任何需要使用字段或在受约束类型上调用方法的代码都将在C ++中执行强制转换为正确的类型。 同样,这里我们依靠C#编译器正确地执行泛型约束,而我们则向C ++编译器说明类型。

          Generic sharing with value types

          价值类型的通用共享

          Let’s jump back now to the HelloWorld_DemonstrateGenericSharing_m4 function and look at the implementation for GenericType<DateTime>. The DateTime type is a value type, so GenericType<DateTime> is not shared. We can jump to the declaration of constructor for this type, GenericType_1__ctor_m10. There we see a #define, as in the other cases, but the #define maps to the GenericType_1__ctor_m10_gshared function, which is specific to the GenericType<DateTime> class, and is not used by any other class.

          现在让我们跳回到HelloWorld_DemonstrateGenericSharing_m4函数,并查看GenericType<DateTime>DateTime类型是一个值类型,因此不共享GenericType<DateTime> 。 我们可以跳转到此类型的构造函数声明GenericType_1__ctor_m10 。 与其他情况一样,在这里我们看到#define ,但是#define映射到GenericType_1__ctor_m10_gshared函数,该函数特定于GenericType<DateTime>类,并且未被任何其他类使用。

          Thinking about generic sharing conceptually

          从概念上考虑泛型共享

          The implementation of generic sharing can be difficult to understand and follow. The problem space itself is fraught with pathological cases (e.g. the curiously recurring template pattern). It can help to think about a few concepts:

          通用共享的实现可能很难理解和遵循。 问题空间本身充满了病理情况(例如, 反复出现的模板模式 )。 考虑以下几个概念可能会有所帮助:

          • Some generic types only share method implementations with themselves (e.g. generic types with a value type generic parameter, GenericType above)

            一些通用类型仅与它们自己共享方法实现(例如,具有值类型通用参数的通用类型, GenericType above )

          • Generic types with a reference type generic parameter are fully shared – they always use the implementation with System.Object for all type parameters.

            具有引用类型泛型参数的泛型类型是完全共享的 –它们始终将System.Object的实现与所有类型参数一起使用。

          • Generic types with two or more type parameters can be partially shared if at least one of those type parameters is a reference type.

            如果两个或多个类型参数中的至少一个是引用类型,则可以部分共享这些类型参数。

          The il2cpp.exe utility always generates the fully shared method implementations for any generic type. It generates other method implementations only when they are used.

          il2cpp.exe实用程序始终会为任何泛型类型生成完全共享的方法实现。 仅在使用其他方法实现时,才会生成它们。

          Sharing of generic methods

          通用方法的共享

          Just as method implementations on generic types can be shared, so can method implementation for generic methods. In the original script code, notice that the UsesDifferentGenericParameter method uses a different type parameter than the GenericType class. When we looked at the shared method implementations for the GenericType class, we did not see the UsesDifferentGenericParameter method. If I search the generated code for “UsesDifferentGenericParameter” I see that the implementation of this method is in the GenericMethods0.cpp file:

          正如可以共享泛型​​类型的方法实现一样,泛型方法的方法实现也可以共享。 在原始脚本代码中,请注意UsesDifferentGenericParameter方法使用与GenericType类不同的类型参数。 当我们查看GenericType类的共享方法实现时,没有看到UsesDifferentGenericParameter方法。 如果我在生成的代码中搜索“ UsesDifferentGenericParameter”,则会看到此方法的实现位于GenericMethods0.cpp文件中:

          1

          2
          3
          4
          5
          6
          7
          extern "C" Object_t * GenericType_1_UsesDifferentGenericParameter_TisObject_t_m15243_gshared (GenericType_1_t2159 * __this, Object_t * ___value, MethodInfo* method)
          {
          {
          Object_t * L_0 = ___value;
          return L_0;
          }
          }

          1

          2
          3
          4
          5
          6
          7
          extern "C" Object_t * GenericType_1_UsesDifferentGenericParameter_TisObject_t_m15243_gshared ( GenericType_1_t2159 * __this , Object_t * ___value , MethodInfo* method )
          {
          {
          Object_t * L_0 = ___value ;
          return L_0 ;
          }
          }

          Notice that this the fully shared version of the method implementation, accepting the type Object_t*. Although this method is in a generic type, the behavior would be the same for a generic method in a non-generic type as well. Effectively, il2cpp.exe attempts to always generate the least code possible for method implementations involving generic parameters.

          注意,这是方法实现的完全共享版本,接受类型Object_t* 。 尽管此方法是泛型类型,但对于非泛型类型的泛型方法,其行为也将相同。 实际上,il2cpp.exe尝试始终生成涉及通用参数的方法实现的最少代码。

          Conclusion

          结论

          Generic sharing has been one of the most important improvements to the IL2CPP scripting backend since its initial release. It allows the generated C++ code to be as small as possible, sharing method implementations where they do not differ in behavior. As we look to continue to decrease binary size, we will work to take advantage of more opportunities to share method implementations.

          自从最初发布以来,通用共享一直是IL2CPP脚本后端最重要的改进之一。 它允许所生成的C ++代码尽可能地小,并在行为无差异的情况下共享方法实现。 当我们希望继续减小二进制大小时,我们将努力利用更多机会来共享方法实现。

          In the next post, we will explore how p/invoke wrappers are generated, and how types are marshaled from managed to native code. We will be able to see the cost of marshaling various types, and debug problems with marshaling code.

          在下一篇文章中,我们将探讨如何生成p / invoke包装器,以及如何将类型从托管代码整理到本机代码。 我们将能够看到封送各种类型的费用,并调试封送代码的问题。

          翻译自: https://blogs.unity3d.com/2015/06/16/il2cpp-internals-generic-sharing-implementation/

          il2cpp

          展开全文
        • il2cppThis is the fourth blog post in the IL2CPP Internals series. In this post, we will look at how il2cpp.exe generates C++ code for method calls in managed code. Specifically, we will investigate s...
        • il2cpp_IL2CPP内部介绍

          2020-09-12 19:55:40
          il2cppAlmost a year ago now, we started to talk about the future of scripting in ... The new IL2CPP scripting backend promised to bring a highly-performant, highly-portable virtual machine to Unity...
        • Il2CppDumper 中文说明请戳 Unity il2cpp逆向工程师 产品特点 完整的DLL还原(代码除外),可用于提取MonoBehaviour和MonoScript ...运行Il2CppDumper.exe并选择il2cpp可执行文件和global-metadata.dat文件,然后
        • il2cpp 这是IL2CPP Internals系列中的第六篇 。 在本文中,我们将探讨il2cpp.exe如何生成用于在托管代码和本机代码之间进行互操作的包装方法和类型。 具体来说,我们将研究可blittable类型和不可bbltable类型之间的...
        • il2cpp 这是IL2CPP Internals系列中的第七篇 。 在本文中,我们将探讨IL2CPP运行时如何与垃圾收集器集成。 具体来说,我们将看到托管代码中的GC根如何与本地垃圾收集器通信。 (This is the seventh post in the IL2...
        • il2cpp 逆向 破解

          2018-08-20 11:59:11
          逆向il2cpp,恢复类名,方法名。分析源代码
        • il2cpp动态调试This is the third blog post in the IL2CPP Internals series. In this post, we will explore some tips which make debugging C++ code generated by IL2CPP a little bit easier. We will see how...
        • Failed running C:\Program Files\Unity2019.2.8f1\Editor\Data\il2cpp/build/il2cpp.exe --convert-to-cpp --emit-null-checks --enable-array-bounds-check --dotnetprofile=“unityaot” --compile-cpp --libil2...
        • 当apk是统一的il2cpp游戏时,可以将apk文件转换为stantard android项目的工具包括钩子api和il2cpp c ++脚手架。 概括 优雅地在apk文件上编写代码。 可以将Apk文件直接转换为Android项目以进行二次开发,因此支持...
        • 当apk是统一的il2cpp游戏时,将apk文件转换为stantard android项目的工具包括钩子api和il2cpp c ++脚手架。 概括 优雅地在apk文件上编写代码。 可以将Apk文件直接转换为Android项目进行二次开发,因此很受支持。 ...
        • 从的输出生成 Managed->IL2CPP 代理程序集的工具。 这允许从受管域使用 IL2CPP 域和其中的对象。 这包括泛型类型和方法、数组和新对象创建。 有些东西可能会被严重破坏。 用法 构建或获取版本 使用获取虚拟程序集 ...
        • 这是一个由三部分组成的微型系列文章中的第一篇,有关IL2CPP AOT编译器执行的一些微优化以及如何利用它们进行了微优化。 尽管这里没有什么可以使代码运行速度提高两倍或三倍,但是这些小的优化可以帮助游戏的重要...
        • il2cpp 热更 上次我们了解到虚拟方法调用比直接调用慢,并且我们发现了如何告诉IL2CPP可以将给定的虚拟方法调用转换(去虚拟化)为更快的直接方法调用。 但是,当您 必须 进行虚拟方法调用 时会发生什么 ? 让我们至少...
        • Il2CppInspector可帮助您对IL2CPP应用程序进行逆向工程,从而提供当前可用的最完整的分析。 主要特点 将IL2CPP类型定义,元数据和方法指针输出为 创建包含在反编译诸如用于使用IL2CPP应用结构和元数据.NET组件垫片...
        • u3d http besthttp1.7.8.rar 修改IL2CPP版本! u3d http besthttp1.7.8.rar 修改IL2CPP版本!
        • IL2CPP Testing

          2020-12-08 23:13:24
          <div><p>Have you tested the project using the new IL2CPP compiler for iOS? Builds using UnityZip, seem to always fail. <p>Here's a bug on Unity's Issue Tracker mentioned by someone else: ...
        • unity打包webgl中出现的错误及解决方法第一个 IL2cpp.exe not run properly第二个 IL2cpp/build/unityLinker.exe not run properly 第一个 IL2cpp.exe not run properly 常见的iL2cpp.exe not run properly,是因为...
        • 文章目录一、前言二、NDK环境配置1、下载NDK2、Unity NDK配置3、NDK环境变量配置三、il2cpp打包四、补充 一、前言 Unity项目需要上架google应用商店。而google要求上架的app必须支持64架构。 如下: 详见此页:...
        • Unity之IL2CPP

          万次阅读 2019-03-16 19:19:25
          背景 ...Scripting Backend有mono和il2cpp两个选项,它们是Unity脚本后处理(Scripting Backend)的两种方式。 概念 IL2CPP 是 Unity一种新的脚本后处理(Scripting Backend)方式,针对.Net平台编...
        • 前文详细的介绍了IL2CPP的来龙去脉,这里用一个实际的例子来看看Unity3D里的IL2CPP都为我们做了哪些工作以及在使用的过程中会遇到哪些问题。 IL2CPP应用的第一个平台是WebGL,为了让游戏可以一键部署到基于WebGL...
        • IL2CPP反汇编程序。 开启Apk 选择apk,它会自动展开和反汇编。 像代码编辑器一样,您可以看到反汇编的结果。 比较Apks 选择两个apk,并显示它们之间的差异。 如果存在差异,则更改背景色。 入门 视窗 下载 打开...
        • il2cpp 源码

          2018-10-23 20:23:43
          Unity3D安装目录下头文件以及源码. D:\Program Files\2017.2.1p2\Editor\Data\il2cpp\libil2cpp

        空空如也

        空空如也

        1 2 3 4 5 ... 20
        收藏数 4,942
        精华内容 1,976
        关键字:

        il2cpp