Crave For Games


The text below is one of the notes from the StoneDrop development log. If you want a list of only technical articles you can go to the Articles page.
Conditional Attribute (C#)

Objective: exclude debug method calls and data computations from release build.

Solution: Conditional attribute.

 

From the very beginning of the StoneDrop development I was aware that methods from the UnityEngine.Debug (docs.unity3d.com/S...[1]) class have a significant impact on game performance. That's why I've created a wrapper class (the adapter pattern, en.wikipedia.org/w...[2]) Logger, which is used instead of Debug. Such an approach allows me to use conditional compilation inside the Logger class and omit all the debug-related code from the methods in release compilation process (figure 1).

160324_define_debug.png

Figure 1. Conditional compilation example in Logger class. The Debug.Log calls now will be compiled out from release builds

 

Nevertheless the approach with the wrapper class still has it's performance penalties even in a release build. For example, the code

Logger.Say(string.Join(", ", intArray.Select(x => x.ToString()).ToArray());

still computes argument for the Say method in the release build. This means the Select, ToString, ToArray and Join will be called anyway and they allocate memory (advancing the CG.Collect call) and waste CPU cycles computing the information that will never be displayed.

 

Today I've discovered the existence of a Conditional attribute (msdn.microsoft.com...[3]) in C#. It allows to exclude not only the body of a method but also the method calls from the compilation. So if we mark Logger.Say method with [Conditional("DEBUG")] the argument computations will be eliminated from the release build (reminder: starting from Unity 5.0 the conditional compilation symbol DEBUG automatically added to all development builds).

To ensure that this is the case I've made a simple test code (figure 2). It works as expected: if the CONDITIONAL_TEST is not defined then myInt always has 0 value which means the argument computations were excluded from the runtime code.

160324_conditional.png

Figure 2. Test code to check the arguments computation of conditional method are excluded from the build

 

Thus the Conditional attribute can be very helpful in providing the debug functionality and excluding it's performance penalties from release builds without source code modification.

 

If you want to use your own compilation symbols keep in mind the method calls are excluded from the code if the compilation symbol is not presented in the file with the call, not in the file with the method declaration/implementation. More details in the official documentation: msdn.microsoft.com...[3].

 

UPD March 2016: Unity doesn't exclude Debug method calls from release builds (though it's implementation is more light-weighted in the release mode). You even can find in the official documentation an advice to use code snippet

if (Debug.isDebugBuild) Debug.Log(message);

for debug purposes.

 

UPD June 2016: using the static Logger class everywhere in the project is a bad architectural design approach in terms of reusability. In such a case every class in the project is dependent on the Logger. Some reflections on this problem are presented in the book "Game Programming Patterns" by Robert Nystrom in the "Singleton" chapter.

 

Link 1: http://docs.unity3d.com/ScriptReference/Debug.html

Link 2: https://en.wikipedia.org/wiki/Adapter_pattern

Link 3: https://msdn.microsoft.com/en-us/library/aa664622(v=vs.71).aspx

This article in social networks: