Search Unity

The scripting virtual machine team at Unity is always looking for ways to make your code run faster. This is the first post in a three part miniseries about a few micro-optimizations performed by the IL2CPP AOT compiler, and how you can take advantage of them. While nothing here will make code run two or three times as fast, these small optimizations can help in important parts of a game, and we hope they give you some insight into how your code is executing.

Modern compilers are excellent at performing many optimizations to improve run time code performance. As developers, we can often help our compilers by making information we know about the code explicit to the compiler. Today we’ll explore one micro-optimization for IL2CPP in some detail, and see how it might improve the performance of your existing code.


There is no other way to say it, virtual method calls are always more expensive than direct method calls. We’ve been working on some performance improvements in the libil2cpp runtime library to cut back the overhead of virtual method calls (more on this in the next post), but they still require a runtime lookup of some sort. The compiler cannot know which method will be called at run time – or can it?

Devirtualization is a common compiler optimization tactic which changes a virtual method call into a direct method call. A compiler might apply this tactic when it can prove exactly which actual method will be called at compile time. Unfortunately, this fact can often be difficult to prove, as the compiler does not always see the entire code base. But when it is possible, it can make virtual method calls much faster.

The canonical example

As a young developer, I learned about virtual methods with a rather contrived animal example. This code might be familiar to you as well:

Then in Unity (version 5.3.5) we can use these classes to make a small farm:

Here each call to Speak is a virtual method call. Let’s see if we can convince IL2CPP to devirtualize any of these method calls to improve their performance.

Generated C++ code isn’t too bad

One of the features of IL2CPP I like is that it generates C++ code instead of assembly code. Sure, this code doesn’t look like C++ code you would write by hand, but it is much easier to understand than assembly. Let’s see the generated code for the body of that foreach loop:

I’ve removed a bit of the generated code to simplify things. See that ugly call to Invoke? It is going to lookup the proper virtual method in the vtable and then call it. This vtable lookup will be slower than a direct function call, but that is understandable. The Animal could be a Cow or a Pig, or some other derived type.

Let’s look at the generated code for the second call to Debug.LogFormat, which is more like a direct method call:

Even in this case we are still making the virtual method call! IL2CPP is pretty conservative with optimizations, preferring to ensure correctness in most cases. Since it does not do enough whole-program analysis to be sure that this can be a direct call, it opts for the safer (and slower) virtual method call.

Suppose we know that there are no other types of cows on our farm, so no type will ever derive from Cow. If we make this knowledge explicit to the compiler, we can get a better result. Let’s change the class to be defined like this:

The sealed keyword tells the compiler that no one can derive from Cow (sealed could also be used directly on the Speak method). Now IL2CPP will have the confidence to make a direct method call:

The call to Speak here will not be unnecessarily slow, since we’ve been very explicit with the compiler and allowed it to optimize with confidence.

This kind of optimization won’t make your game incredibly faster, but it is a good practice to express any assumptions you have about the code in the code, both for future human readers of that code and for compilers. If you are compiling with IL2CPP, I encourage you to peruse the generated C++ code in your project and see what else you might find!

Next time we’ll discuss why virtual method calls are expensive, and what we are doing to make them faster.

60 评论



  1. Hi, does this optimalization work if I call this.Speak() inside of Cow class?
    Does this optimalization work in Mono as well?

    1. Yes, this optimization is independent of where the method is called from, it would work in any case. Mono also does this optimization.

    2. A related more complicated example: Does boxing get optimized out with generics in both mono and IL2CPP?

      T Convert(U other)
      return (T)(object)(other);

      1. I’m not sure if the box opcode is optimized out here. I don’t believe that the code generation for IL2CPP or Mono will do this, but the C# compiler might. Have a look at the IL code in the assembly generated by the C# compiler, and that should make things clearer.

  2. Thanks for letting us know about the advancement in this area and answering all the questions Josh!
    One thing that is a bit unclear to me… Here: it’s mentioned .Net 3.5 but in profiles there is only 2.0 and 2.0 Subset. Is it the compiler supports 3.5 and for Unity it’s enabled only 2.0?

    1. This .NET profile numbering can be a bit confusing. Although in Unity the profiles are named 2.0 and 2.0 Subset set, they are roughy equivalent to .NET 3.5 (there are a few minor differences, probably not important enough to mention). This is the case both with and without the new C# compiler. The C# compiler upgrade does not change the .NET profile version or the C# language version Unity supports (those improvements will be coming later).

      The difference in names is there for historical reasons, and we’ll use the proper name for .NET 4.6 (and later) when that is released.

  3. Finally, sealed is used for optimization! As far I know, Microsoft guys had some low-priority plans to use it for optimization in their JIT but never got down to it.

  4. Nice article but some questions to you:
    Assume that we have a class derived from Cow

    public class FlyingCow : Cow{
    public override string Speak() {
    return “Moooooooo”;

    and class hierarchy is next:
    Animal (base class) -> Cow -> FlyingCow

    in this case we need use “sealed” keyword ONLY in “top” class (in my example FlyingCow) and this keyword will not work in Cow class (IL2CPP will generate C++ code with template) RIght ?

    1. If I’m not mistaken, there are 2 possible cases, depending on the specifications of your Flying cow: does it make the same sound than the Cow or not?

      1) If they make different sounds (“Moo” vs “Moooooooo”), it’s the case you state. Flying cow derives from Cow, so obviously the Cow class can’t be sealed. You can only seal the Flying cow class, so just as you stated this class will be the only one benefiting from the devirtualization.

      2) Now, if they make the same sound (“Moo”) but the difference lies somewhere else in the class, what you can do is: seal the Flying cow class just like before so it still benefits from the entire devirtualization; but also seal the method Speak() on the Cow class (and of course don’t override it in the Flying cow class, because it’s the same) and then it should still be devirtualized.

      1. Le_Nain answered it better than I could, thanks!

  5. C++ also offers virtual and override features. Why implementing a “virtual function invoker” instead of simply translating C# to C++ classes? As I understand, in pure C++ the difference then would be just a matter of 4 lines of assembly code against 1. Is that also the case explained in the article?
    Thanks! Best regards :)

    1. This is an interesting suggestion, and is something we have considered. We don’t use C++ virtual functions for two reasons. First, IL2CPP generates all function as C++ free functions. Doing so simplifies our code generation logic and allows us to do things like generic sharing to optimize for better binary size. Second, the requirements of a managed runtime with respect to virtual functions are a bit more strict than they are for C++. For example, IL2CPP needs to throw an exception when a virtual method call is made for a method that was not generated at compile time. See this post for more details:

  6. Hi Josh, thanks for post.

    About your example, how about using interface to instead of abstract class/method?

    Something like:
    IAnimal animal = new Cow();

    Is that the same problem for generating c++ code?

    1. I’ve not tried this myself, but IL2CPP should be able to devirtualize the method call in this case as well.

  7. Hi Josh,

    Do you know have any information on what happens to unused code? I have in my project tons of debug and test code that will never be called in production, but when I check the assemblies (inside the android APK), all that code is still there.

    I wonder if the IL2CPP or the platform compiler removes unused code. I expect that less code would make the game launch a bit faster and the package smaller as well.


    1. Yes, the IL2CPP build toolchain includes a managed code stripper. So any managed code in IL assemblies that is not used will be removed. This applies only to assemblies, though. Script code in your project is not stripped. If you have script code in your project which is not used, can you manually remove it, or use #if guards to conditionally include it in a build.

      Also, the native code linker will usually remove unused code, so even if the managed code remains, the generated C++ code which is not used will likely not be present in the final binary.

  8. Apologies for the double post. I thought that something went wrong when I submitted the first comment.

    1. No worries! C++ scripting is not on our roadmap right now. But as I mentioned, we’ve had a number of discussions about it in the past.

  9. C++ scripting would be great.

  10. C++ scripting would be awesome!

  11. So this optimization only saves you time if you are calling the method from the inherited class, correct? It does not benefit when you call Animal.Speak()?

    1. Correct, this does not apply to a call via the Animal class. That must always be a virtual method call.

  12. Any chance we will see IL2CPP on PC standalone builds anytime soon? I know it’s in the Windows Store builds already… what’s the hold up? Our Garbage Collection is causing hitches and I’ve heard IL2CPP will help that a lot. Love the blog post, by the way.

    1. I’m glad you liked the post, I enjoyed writing and the participating in this discussion!

      I don’t think that IL2CPP will be available for PC standalone builds in Unity 5.5. It may be available later, but I don’t have any firm information about that now. The hold-up is due to a few issues:

      – We need a good story for installing and locating platform-specific C++ compilers on the machine running the Unity editor. We don’t have that yet.
      – We need to add some additional support for parts of IL2CPP that are not implemented because they don’t occur on mobile and console platforms (where IL2CPP ships now) too often, e.g. external process handling
      – We need to prepare to support the additional platforms from the QA and documentation end.

      All of these are hurdles that we can overcome, as with many things in software development, it is all a matter of resources and priorities. We want to get to the point where this can be released though.

      With that said, I’m not sure that IL2CPP will help the garbage collection hitches you are seeing. We’ve not had reports of IL2CPP significantly improving GC pauses on other platforms. IL2CPP does use a newer version of the Boehm GC than the Mono that ships with Unity does, but it is still a conservative GC.

      Actually, our team is currently focused almost entirely on the Mono runtime upgrade, which is the next big step in getting an incremental GC for Unity. Note that the Mono runtime upgrade may not come with a new GC initially (we have other, non-Mono work to do to make a new GC work), but it is a prerequisite.

      So although we’re not working on IL2CPP for standalone now, we are working what will hopefully solve the GC hitch problem you see!

      1. Robert Cummings

        七月 28, 2016 2:10 上午

        Thanks for the article(s)! Very interested in performance gains above all other things in Unity as more perf means I can do more things. With that in mind I’d love to see an article on the GC improvements you’ve mentioned! the GC has been a thorn for a decade or more :)

        1. Unfortunately we don’t have any GC changes to discuss now. Sorry if I can misleading! However GC improvements are coming, after the Mono runtime upgrade, so stay tuned.

  13. Does this have any effect on classes directly inheriting from MonoBehaviour, like Farm in the example?
    I know the usual Unity methods like Start, Awake and Update aren’t overrides of an abstract method, but is there something else thats ‘abstract’ in MonoBehaviour?

    1. This should not have any impact on classes deriving directly from MonoBehaviour. The MonoBehaviour does not have any virtual methods, so Unity does not call any of the methods you might implement on a class like Farm via the virtual method call process.

  14. Jackie Engberg Christensen

    七月 27, 2016 1:57 下午

    Must admit this is a pretty interesting read as I’m very interested in the IL2CPP, although I’m not sure how it entirely works behind the covers. Additional I’ve been wondering for some times why Unity doesn’t simply allow you to make C++ script which can be used like the C#/UnityScritpt/boo scripts native, any wouldn’t this over all be a huge performance gain for skilled C++ programmers?

    1. You can find out more about how IL2CPP internally in this blog post series:

      C++ scripting has been an active topic of conversation both inside and outside of Unity for some time. Opinions about its ease of use and performance impact in Unity vary, so I can’t really speak to them. I can give some specific reasons why C++ scripting is not supported in Unity now though.

      – The Unity API is pretty large and exposed for managed code now. Supporting and documenting this API in C++ is a really large task.
      – The Unity editor reloads script code on the fly when you go in and out of play mode. This is pretty difficult to reliably do with C++ code.

      I don’t think that Unity will support C++ scripting any time soon, but this is just my opinion, things are always subject to change. If you need C++ for performance in some specific case, it is possible to use native code plugins with Unity, although they don’t have access to the Unity API.

      1. Jackie Engberg Christensen

        七月 27, 2016 5:52 下午

        Pretty nice post and further insight in IL2CPP.

        It makes fine sense why C++ isn’t supported at this point, let alone from the things you’ve mentioned, but it would be a nice addition at some point I feel like, but I get it that new features in the engine and tool is much more important and preferable :) – Cheers!

  15. Is the best way using

    foreach (var animal in animals)

    or may be

    for(var i=0; i<animals.Count; i++)


    1. I found these the fastest versions of “for”, since list.Count is evaluated only once; rather than every loop iteration.

      for (int i=list.Count-1; i>=0; –i) …
      for (int i=0, iend = list.Count; i<iend; ++i) …

      Where the decrementing version could run faster on platforms that support the "subs" instruction (eg ARM devices), which means there could be one "cmp" operation less per loop-iteration.

      foreach is often considered the slowest method to iterate over a list, because it allocates an enumerator and it calls various methods each iteration (Next and Current).

      However, you will never know, if you don't profile your code.

    2. This is really the best advice for any performance question:

      > However, you will never know, if you don’t profile your code.

      But of course the answer depends on what you mean by “best”. I find the foreach loop to be more readable than the for loop, so I think it is the best from a source code perspective. Looking at the IL code generated in each case (I encourage you to try this using a tool like ILSpy), it seems that the for loop generates smaller IL code, and Peter indicated.

      For performance issues, never afraid to question, investigate, and measure. I’m often surprised what I find.

  16. Why can’t you check if class doesn’t have subclasses and then don’t require “sealed” keyword? Linked libraries?

    1. That is certainly possible, and in this example, looks to be pretty easy. In general for .NET code, we cannot make the assumption that a given virtual call will resolve to a specific method because assemblies can be loaded at runtime, so the compiler must play it safe and make the virtual method call for classes that are not sealed.

      With IL2CPP the situation can be a little different, since IL2CPP is an ahead-of-time compiler that does not allow assemblies to be loaded at runtime. It does know all possible code that could derive from Cow at compile time. However, we’ve not implemented any whole-program optimization like this in IL2CPP, so it does not have the intelligence to make this decision (although it theoretically could).

      1. I feel like in the time it took to explain the ‘sealed’ keyword in this blog, it would have been possible to write an auto-sealer pass for IL2CPP. Not that knowing about ‘sealed’ is a bad thing, but it just seems fairly trivial and would be one less thing for people to worry about having to remember to do.

        Is it more than just looping over all types to mark them ‘sealed’ by default and then doing a second pass and if the type has a base, clear the sealed flag on the base?

        1. Well, I think it might take a little longer to write an auto-sealer than it would to read this blog post, at least for me. :)

          This does bring up a good point about whole-program optimization though. For ahead-of-time compilation, whole program analysis of the IL code like this is an option. I don’t think it is something that IL2CPP should do, since it is already complex enough as a transpiler for IL code to C++ code. I do think there is room for this sort of optimization pass in a separate tool though.

        2. Sorry, I forgot that this was at the IL level, and not in the actual C# compiler. It *should* be trivial if you were able to do it in the compiler before it generated IL (would be nice if there was already ‘sealed’ assembly support). Though I think it should still be possible in IL2CPP, but it means modifying IL instructions to change a callvirt to a regular call which probably isn’t fun at that level.

          One other question — does IL2CPP attempt to generate inline-able C++ functions? Or is it just relying on the C++ compiler’s WPO support to inline functions?

        3. You’re correct, it is possible for IL2CPP to do this. So far, we’ve not added an IL pre-processing pass to IL2CPP, so I would rather see it in a separate tool.

          As far as inlining for generated C++ functions is concerned, IL2CPP does’t do anything special to try to get generated functions to be inlined. However, the generated C++ code does end up in a few (relatively speaking) large .cpp files. The C++ compiler then has a lot of code to work with in each translation unit, and can usually do a good job of inlining functions.

  17. Johnathan Rossitter

    七月 27, 2016 5:06 上午

    Great Article, please keep more like this coming.
    I know you plan to have other optimizations discussed, but can you point to a resource (like in the documentation) that talks more about what we can do to make our coding practices better for cpp?

    1. Thanks, we have two more articles ready now – they will be publish one for each of the next two weeks.

      We don’t have any documentation about writing C# code to produce better C++ code, because, well, producing good C++ code is our job. :)

      In all seriousness, we would like to you write good C# code as you normally would, just follow good C# coding practices. Te IL2CPP, Mono, and .NET scripting backends should generate the best machine code possible. This has not always been the case, for example, the C# compiler currently shipping with Unity will generate unnecessary allocations for some foreach loops, so you should prefer for loops instead. We’re fixing this specific issue with a new C# compiler in Unity 5.5 (, so you can go back to writing foreach loops and not worry about their cost.

      This is the approach we’re going for in general, good C# code should produce good machine code.

  18. Marc-André Jutras

    七月 27, 2016 3:17 上午

    One day I would love to know the purpose of;

    int32_t L_6 = V_3;
    int32_t L_7 = L_6;

    Just passing int around? Sounds wasteful.

    1. I’m a complete amateur in this area but I believe it has something to do with the way SSA (Static Single Assignment) improves code analysis,

    2. As I’ve thought more about this, I bet that it would be fun to dive deep on this question, but the C# code all the way down to the generated assembly code. I feel another blog post coming…

      By way of a brief explanation, I’ll say this. IL is a stack-based virtual machine, and the C++ code generated by IL2CPP represents that stack using local variables in C++ methods. In order to correctly track the IL stack manipulations, IL2CPP will often generate code like this which seems unnecessary.

      Indeed, from the C++ source code side, this is wasteful. However, the C++ compiler will optimize these two instructions, so the assembly code that is actually executed will be no different than it would for a single C++ instruction.

  19. Nice post, however i am not sure what tangible gains will something like this give. Is the cost of virtual vs. direct method call in IL2CPP really that different?

    1. Marc-André Jutras

      七月 27, 2016 3:32 上午

      Yes, the difference is quite notable when in a restrictive environment like iOS, DS or PSP.

      A VTable lookup can eat quite a few valuable cycle that would be better used somewhere else. I worked on a PSP game where class hierarchy were destroyed and merged on purpose to reduce the number of lookup in code path that were most used per frame.

    2. As usual with performance measurement, tangible gains really depend on the specific hardware and code involved. In the next post I’ll discuss the cost of virtual method invocation a bit more and look at something we’ve done in IL2CPP to make that cost less.

      In general (not specifically with IL2CPP) virtual methods do cost more than direct methods. A direct method call is usually a single assembly instruction that jumps to a hard-coded address. For a virtual method, at least a few more assembly instructions are required, since that address cannot be known at compile time.

      But yes, nothing here will drastically improve performance. Most games we see are not CPU-bound. With that said though, every cycle saved for things like method calls, which are not important to the player, is another cycle that can be used for better graphics rendering or improved AI, or other things that do matter to the player. Our focus on the VM team is to make things like this as cheap as possible to give you more time per frame for the stuff that matters to the player.

  20. Grate Thanks. And If is posible, a video tutorial introduction to IL2CPP in the learning live training section?

    1. Alan, What kind of information are you interested in seeing in a video? We have a few other resources about IL2CPP, first in a Unite 2015 presentation about debugging and profiling IL2CPP ( and also in a blog post series about IL2CPP internals (

      I’d love to hear suggestions about what else you would like to see though. Thanks!

      1. Thanks for listening. I need to optimize and run faster same cpu expensive code but I’m a beginner. I wish to run it in c++. I have never use IL2CPP so I do not know what I’m talking about. In my case I am Pc standalone user so i will wait for IL2CPP patiently.
        Since IL2CPP is not yet available for PC standalone builds then is not necessary to make a new C++ scripting section in . But in there is no C++ or IL2CPP video at all. And can be useful in “LIVE SESSIONS ON SCRIPTING” a introduction video about “GETTING STARTED WITH IL2CPP” for how do not know how to implement it in a simple way made by Adam Buckner, Matthew Schell or Mike Geig. Or include the more advance video “Unite 2015 – IL2CPP: Profiling and Debugging” into scripting topic. But this video is a bit too advance for a beginner like me.

        1. Alan, I’m glad you are programming and using Unity. Welcome to the community (hint: it’s a great community of people!)

          We don’t have any information on the Learn site about C++ scripting because it is not possible in Unity. And we don’t have much about IL2CPP, especially at the beginner level, because you should not have to care about it! If I’m doing my job right, the IL2CPP scripting backend should “just work” for all of your code in Unity.

          Starting off, I would focus on writing your code in C# with Unity. Once you have it working properly, then use a profiler (like the one built into Unity) to see where the performance hot spots are. Only then should you consider moving anything to C++ code.

  21. Pretty neat optimisation. And kinda obvious too, but appreciated nonetheless ;-) . At this point I’m wondering if you’re beginning to see the value in open sourcing IL2CPP. The community can figure out many more optimisations at many times the speed at which you’re currently doing it. Do the cons really outweigh the pros of open sourcing?

    1. Yes, this looks pretty obvious to the human reader. The IL2CPP code conversion utility has a bit more to consider though, but could probably be changed to to this without the sealed keyword, at least in theory.

      Internally, we’ve had many discussions about open sourcing IL2CPP for a while now. We do see the value – that has never been the problem. There is a cost on our side to open sourcing it though. At the moment, that cost is large enough to cut into other priorities (like the .NET Upgrade, for example), so we’re focusing elsewhere now.

      Of course the fastest way to get access to the IL2CPP source code and to improve it is to come work with our team. :)

      1. What is this IL2CPP code conversion utility? Will it make our games and code easier to hack?

        How much harder in general is IL2CPP to hack over Mono? Will it make things more difficult to hack? Are there any tips and tricks on how to make IL2CPP code more robust to combat hackers? I’m sure Unity is aware of the massive amounts of piracy taking place in our Unity games, so wondering if UNity is doing anything to help mitigate this?

        I know it’s not possible to prevent hacking and piracy, I just want to know what is being done to make it more difficult.

        1. Marc-André Jutras

          七月 27, 2016 3:26 上午

          As it’s name imply, it convert IL (Intermediate Language, what our C# compile into) to C++ (or C Plus Plus, CPP). It then goes the normal compilation pipeline C++ follows, which is at the end to produce native machine code. It makes your code far much harder to hack. It is also far faster to run than IL code.

          IL can just be easily read by any IL Inspector utility. Not so much with compiled code.

        2. Marc-André provided a great answer here. If you want more details about how IL2CPP works internally, check out this blog post series:

      2. Marc-André Jutras

        七月 27, 2016 3:20 上午

        I would work at Unity just for the purpose of making the inspector able to copy/paste values… and handling sub-component/composition pattern. :p

      3. Points taken. Good to know you’re focusing on the .net profile upgrade. Is there a chance we’ll get an ETA for this? Will it be in the 5.5 timeframe? Are we to expect significant performance gains even with the initial release with the old GC?

        As for working on IL2CPP, I guess I’ll just wait for the inevitable open sourcing of the transpiler even if it happens in 2 years ;-)

        1. We will let you know an ETA for the .NET profile upgrade as soon as we know when it will be ready. I don’t think it will be in the 5.5 timeframe though, as our internal cut-off for new features in 5.5 is quickly approaching.

          So far we’ve not seen performance gains from the new Mono runtime on the platforms where we have it working. However, we’ve only tested it on some internal projects so far, and not on anything very big yet. I don’t anticipate performance gains before we get to the better GC though.