5. C# compiler
• The VM is an abstract stack machine
• Then, we compile
• Source file goes through
• Tree builder that results into an
• Abstract syntax tree that goes into the
• Attribute evaluator that turns into an
• Attributed tree that then feeds the
• Tree walker that finally generates the
• IL
13. .NET 4.6 FAQ
• What platforms does this affect? All of them, but in different ways:
- Editor and Standalone use the new version of Mono when this option is enabled.
- All console platforms will only be able to use IL2CPP when targeting the new .NET version.
- iOS and WebGL will continue to be IL2CPP only.
- Android will continue to support both Mono and IL2CPP.
- Other platforms are still undergoing work to support either new Mono or IL2CPP
• What about IL2CPP? IL2CPP fully supports the new .NET 4.6 APIs and
features.
14. .NET 4.6 FAQ
• What about the a new GC?
The newer Mono garbage collector (SGen) requires additional work in Unity
and will follow once we have stabilized the new runtime and class libraries.
• Why are my builds larger with the new .NET version?
The .NET 4.6 class libraries are quite larger than our current .NET 3.5 class
libraries. We are actively working on improving the managed linker to
reduce size further.
16. What is ‘Unmanaged’ Code?
• As the name implies, unmanaged code means YOU are responsible for
a host of things, that are usually taken care of for you when using
managed code. For example:
• Memory management
• Thread Synchronization
• Security
• Life-time control of objects
• Etc.
17. Using Unmanaged Code
Access managed through DLL / Libraries
[DllImport (“YourCode”)] followed by a function name, which needs to be
static and extern
Place a plugin directory alongside the ‘Assets’ folder in your project and
place the library there
If you get a “DllNotFoundException” make sure the path is correct and
your library is present in the Plugins directory.
18. DLL in Unity
Put your plugins in :
• Assets/Plugins
• Assets/Plugins/x86
• Assets/Plugins/x86_64
Once you have create / compiled and place
your DLL / library in the right place, you can
call it directly from your managed code.
19. Invoking Unmanaged Code
• Generally speaking, you just call the method associated with the DLLImport tag.
• Using GetProcAddress(), the specific function is looked up and executed.
• Unfortunately, things aren’t that simple (surprise!)
• C ABI is used for most calls.
• Makes it near impossible to call functions that don’t adhere to this, like C++.
• Again, ABI’s are platform specific.
• Use either __stdcall and __cdecl for VC++ or __attribute__((stdcall)) and
• __attribute__((cdecl)) for GCC.
• If you need to invoke C++ code, use ‘extern “C”’ to ensure the calling convention is adhered to.
20. Exceptions
• Runtime Exceptions can happen and unmanaged code needs to be able
to deal with it.
• Unfortunately, C doesn’t support exceptions
• C++ does to some extent but has a different mechanism.
• Don’t cross the streams and let exceptions propagate between
managed and unmanaged code or you run the risk of things going bad
21. Marshalling
• Marshalling is the process of ‘converting’ parameters from managed to
unmanaged space.
• For simple types, this just becomes a straight copy (byte, int, floats,
etc)
• Sometimes called ‘blitting’
• Bool, Strings, Arrays are a little more complex….
• Depends on what type of string it is:
• Ascii, UTF-8, UTF-16, etc.
• Memory boundaries need to be kept separate!
• Don’t keep references around to managed data!
• Data is usually released after call, with obvious consequences.
• Possible to lock a memory area by using the C# ’fixed’ statement.
• Generally, memory is copied around!
22. Marshalling (Cnt’d)
• Strings, Classes and Structs
• As mentioned earlier, strings are handled different than blittable data
types.
• CLR doesn’t just look for single function but function based on the
type. I.e. Different functions for different objects passed in.
• In order to facilitate this, you can ‘MarshallAs’ and describe your
own datatype, as follows:
[DllImport (”somedll")]
private static extern void Foo (
[MarshalAs(UnmanagedType.LPStr)] string ansiString,
[MarshalAs(UnmanagedType.LPWStr)] string unicodeString,
[MarshalAs(UnmanagedType.LPTStr)] string platformString
);
23. Marshalling (Cnt’d)
• Structs and classes can’t be passed by value, only by reference.
• Since it’s managed memory, anything that’s copied may move / change.
• Generally a bad thing to do anyway!
• Structs and classes differ by alignment.
• Structs are sequentially aligned by default (as you’d expect)
• Classes may or may not be laid out in memory the way you defined them!
• If you must have them sequential, use the [StructLayout
(LayoutKind.Sequential)] tag.
• Return values should be copied back into managed memory.
24. Memory Management / Custom
Marshalling
• Memory managed owned by CLI will be managed by CLI
• I.e. a reference to an object passed down to unmanaged code will be
reclaimed by the CLI.
• Both managed and unmanaged memory allocations come from the
same pool.
• Use Marshall.AllocCoTaskMem() and Marshall.FreeCoTaskMem()
• Rather than having built-in Marshalling, you can use create custom
ones.
• Use ‘MarshalAs’
• Remember that you’ll need to write implementations for all variations
of objects passed in!
25. Bottom Line
• Don’t overlook the impact Marshalling has on your code.
• You’re copying memory around!
• Make sure you know who owns what. I.e. The CG doesn’t know what
you own / use.
• Unless you have a valid reason for using unmanaged code, stick to
managed code.
26. Example of marshalling
struct Boss {
char* name;
int health;
};
int SumBossHealth(Boss* bosses, int size) {
int sum = 0;
for (int i = 0; i < size; ++i) {
sum += bosses[i].health;
}
return sum;
}
bool IsBossDead(Boss b) {
return b.health == 0;
}
30. Best practices - Boxing
using UnityEngine;
using System.Collections;
public class InputAxis : MonoBehaviour
{
void Update ()
{
float x = Input.GetAxis("Horizontal");
Debug.Log(x);
}
32. Best practices - Foreach
int ForEachArray (int[] items)
{
var total = 0;
foreach (var item in items)
total += item;
return total;
}
33. Best practices - Foreach
int ForEachArray(int[] items)
{
int num = 0;
for (int i = 0; i < items.Length; i++)
{
int num2 = items[i];
num += num2;
}
return num;
}
34. Best practices – Foreach on collections
To avoid allocations when using foreach over a collection the following criteria need to be
met:
- For collections of value types, the collection and enumerator implement the generic interfaces
IEnumerable<T> and IEnumerator<T>.
- The enumerator implementation is a struct not a class.
- The collection has a public method named GetEnumerator whose return type is the enumerator
struct.
The BCL types System.Collections.Generic.List<T>, System.Collections.Generic.Dictionary<T>, and
System.Collections.Generic.HashSet<T> all follow the guidelines above and should be safe to
perform foreach statements on.
35. Best practices – Foreach on collections
int ForEachCustom (CustomCollection items)
{
var total = 0;
foreach (int item in items)
total += item;
return total;
}
Specify the type explicitely -> public class Enumerator : IEnumerator<int>, not just an Enumerator
on Systems.Objects (by default).
Use a struct and not a class so your when you do new in GetEnumerator, it goes on the stack and
not the heap -> return new Enumerator (this);
36. Best practices – Foreach on collections
class CustomCollection : IEnumerable<int>
{
private readonly int[] _items;
public CustomCollection(int[] items) { _items = items; }
public Enumerator GetEnumerator ()
{ return new Enumerator (this); }
IEnumerator<int> IEnumerable<int>.GetEnumerator ()
{ return GetEnumerator (); }
IEnumerator IEnumerable.GetEnumerator ()
{ return GetEnumerator (); }
public struct Enumerator : IEnumerator<int>
{
private readonly CustomCollection _collection;
private int _index;
[...]
}
}
37. Best practices
public class ClassVsStruct : MonoBehaviour {
struct PointStruct
{ public int x; public int y; }
class PointClass
{ public int x; public int y; }
void Update()
{
PointStruct ps; ps.x = 3; ps.y = 4;
PointStruct ps2 = new PointStruct(); ps2.x = 7; ps2.y = 8;
PointClass pc = new PointClass(); pc.x = 5; pc.y = 6;
gameObject.transform.position.Set( ps.x + pc.x, ps.y + pc.y, 0);
}
}
39. Best practices
• Strings are immutable and will make copies
• Use String.Builder
• Use a hash to avoid Strings
static readonly int material_Color = Shader.PropertyToID(“_Color”);
static readonly int anim_Attack = Animator.StringToHash(“attack”);
material.SetColor(material_Color, Color.white);
animator.SetTrigger(anim_Attack);
40. The GC.Collect myth
• There’s a myth saying that when you want to call GC.Collect, you should
do it 6 times.
• Another myth says it’s 7 times.
• Is that really true?
41. The GC.Collect myth
• On some platforms the GC can decommit memory from virtual memory
• And that happens after so many collections without some pages being
needed
• But it can never be used by anything other than GC because it is still
reserved … that address range is not available for any other allocator in
the process
• tldr; There isn’t much benefits. Unless you do something wrong and want
to be nice to other applications running on that OS.
44. GameManager pattern
• In this use case, there is a very important lesson to learn. But to show you
the lesson, I will do a very common mistake…
• The Singleton is a very good candidate that can be called thousands of
times per frame
• Let’s look at one way to implement it.
59. Conclusion
• Some C# keywords are hiding overhead and allocations
• Prefer POD and struct of classes
• int, float, struct
• NOT class, NOT strings
• Check the resulting IL
• But more importantly than anything else …