.NET and me Coding dreams since 1998!

5Aug/077

Releasing the build

I've been investigating today different effects of some csc.exe (C# compiler) compiler switches and came up with the really interesting conclusion

CSC.exe

C# compiler compiler has two important switches I pick for my today's investigation:

  • optimize, with values -/ +
  • debug, with values   -/+/full/pdbonly (+ and full are the same setting)

When you create a new C# project in Visual Studio;

  • Debug configuration is set up to use: /optimize- /debug:full
  • Release configuration is set up to use /optimize+ /debug:pdbonly

(To see it for yourself check out Output window after starting the build in release and debug build)

What I tested

I pick four different scenarios for my little experiment:

  • /optimize- /debug:full (debug configuration of visual studio )
  • /optimize+ /debug:full (optimized code with complete debug info setting)
  • /optimize+ /debug:pdbonly (release configuration of visual studio)
  • /optimize+ (typical "release" build setting used while building manually - command prompt, Nant etc)

How did I test it

I was using in my tests very simple console application which only  purpose is to have a property (to check optimization) and to throw an exception (to check stack trace)

using System; 

namespace TestApp

{   

class Program   

{       

     static void Main(string[] args)       

     {           

         int i = 5 / Convert.ToInt32(Test);       

     }        

     private static string _test;        
    
public static string Test
       
    
{
           
       
get { return _test; }
           
       
set { _test = value; }
       
    
}
    

}

}

After every build I was:

  1. Checking out for IL code optimization using ILDASM.exe to check out if the property code has been inlined and that there are no NOP IL statement at the beginning )
  2. Checking if stack trace shows the line number
  3. Checking if debugger can be attached to process

Test 1: /optimize- /debug:full

This one was just done for warming up, because we all know what debug configuration in visual studio does

Command line: csc /t:exe /debug:full /optimize- program.cs

File data

TestApp.exe 4608 bytes

TestApp.pdb 13824 bytes

Optimization check

Test1_IL

As you can see there are 10 lines of code and IL_0000 contains NOP. Clearly, not optimized IL code

Debugger check

I succeed in attaching debugger to cp

Stack trace check

System.DivideByZeroException was unhandled
  Message="Attempted to divide by zero."
  Source="TestApp"
  StackTrace:
       at TestApp.Program.Main(String[] args) in D:DocumentsDocumentsBlog materialCSCTestAppTestAppProgram.cs:line 9
       at System.AppDomain._nExecuteAssembly(Assembly assembly, String[] args)
       at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
       at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
       at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
       at System.Threading.ThreadHelper.ThreadStart()
  InnerException:

There is  line number data in stack trace, which is also expected

Test 2: /optimize+ /debug:full

File data

TestApp.exe 4096 bytes

TestApp.pdb 13824 bytes

Optimization check

Test4_IL

IL code is optimized

Stack Trace

System.DivideByZeroException was unhandled
  Message="Attempted to divide by zero."
  Source="TestApp"
  StackTrace:
       at TestApp.Program.Main(String[] args) in D:DocumentsDocumentsBlog materialCSCTestAppTestAppProgram.cs:line 9
       at System.AppDomain._nExecuteAssembly(Assembly assembly, String[] args)
       at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
       at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
       at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
       at System.Threading.ThreadHelper.ThreadStart()
  InnerException:

So the stack trace shows line number.

Test 3: /optimize+ /debug:pdbonly

Command line: csc /t:exe /debug:pdbonly /optimize+ program.cs

File data

Program.exe  4096 bytes

Program.pdb 11776 bytes

Optimization check

Test2_IL

As we can see on the ILDASM screen shoot code is optimized (property is inlined and no NOP)

Stack trace

System.DivideByZeroException was unhandled
  Message="Attempted to divide by zero."
  Source="TestApp"
  StackTrace:
       at TestApp.Program.Main(String[] args) in D:DocumentsDocumentsBlog materialCSCTestAppTestAppProgram.cs:line 9
       at System.AppDomain._nExecuteAssembly(Assembly assembly, String[] args)
       at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
       at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
       at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
       at System.Threading.ThreadHelper.ThreadStart()
  InnerException:

There is a line number in stack trace. Great!

Test 4: /optimize+ /debug-

Command line: csc /t:exe /debug:pdbonly /optimize+ program.cs

File data

Program.exe  3584 bytes

Program.pdb  N/A

Optimization check

Test3_IL

Results totally expected for the release build - optimized IL code

Stack trace

at TestApp.Program.Main(String[] args)

No stack trace information

Conclusions

Test result analysis

Test case 4 (release build with debug- switch produces the smallest program.exe: 3584 bytes)

Test 2 (debug:full) and Test 3 (debug:pdbobnly) with optimize+ produced optimized code but the assembly file size increased to 4096 bytes

The size increase comes from the additional assembly manifest declaration of the DebuggableAttribute; IL code is exactly the same in test cases cases 2,3,4.

DebuggableAttribute is initialized with OR values of DebuggingModes enumeration

[Flags]

public enum DebuggingModes

{

Default = 1,

DisableOptimizations = 0x100,

EnableEditAndContinue = 4,

IgnoreSymbolStoreSequencePoints = 2,

None = 0

}

If we would take a look at test cases 2 and 3 with ILDASM we would notice that both of them have in their manifests

debug: pdbonly

// --- The following custom attribute is added automatically, do not uncomment -------
//  .custom instance void [mscorlib]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 02 00 00 00 00 00 )

debug:full

// --- The following custom attribute is added automatically, do not uncomment -------
//  .custom instance void [mscorlib]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 03 00 00 00 00 00 )

Or if we would visualize the same initialized flag values by using Reflector

debug:pdbonly

dbgPdbOnly_Manifest

debug:full

dbgFull_Manifest

The difference between the test case 2 (full) and test case 3 (pdbonly) is that with full debug switch compiler initialize the Debuggable attribute with DebuggingModes.Default value.

 

According to the MSDN: DebuggableAttribute.DebuggingModes Enumeration:

DebuggingModes.Default value

"Instructs the just-in-time (JIT) compiler to use its default behavior, which includes enabling optimizations, disabling Edit and Continue support, and using symbol store sequence points if present."

DebuggingModes.IgnoreSymbolStoreSequencePoints

"Sequence points are used to indicate locations in the Microsoft intermediate language (MSIL) code that a debugger user will expect to be able to refer to uniquely, such as for setting a breakpoint. The JIT compiler ensures it does not compile the MSIL at two different sequence points into a single native instruction. By default, the JIT compiler examines the symbol store in the program database (PDB) file for a list of additional sequence points. However, loading the PDB file requires that the file be available and has a negative performance impact. In version 2.0, compilers can emit "implicit sequence points" in the MSIL code stream through the use of MSIL "nop" instructions. Such compilers should set the IgnoreSymbolStoreSequencePoints flag to notify the common language runtime to not load the PDB file."

According to  Rick Byers - DebuggingModes.IgnoreSymbolStoreSequencePoints this flag setting informs the compiler to use all available space in code (places where IL evaluation stack is empty, or on any "nop" instruction is generated) to store implicit sequence points (internal pointers to PDB locations) into the resulting assembly which results with performance boost of the release build.

Final conclusion on test cases

There is no significant differences between the test cases 2 (full) and 3 (pdbonly) from the performance perspective

How-To:Releasing the builds

Build all of your release builds with /optimize+ /debug:pdbonly switch.

1) Your IL code would be optimized, so no performance hit would occur

2) The stack class would get access to the line information, and make your release builds "debug like"

3) The visual studio IDE release configuration default setting is set to that value

4) I couldn't find any information on net that effects of emitting DebuggableAttributes to assembly manifest causes any significant performance in NET 2.0

Technorati tags: , , , , , , ,


Share this post :

Comments (7) Trackbacks (0)
  1. Thanks for the great article. This is exactly what I was looking for. It would still be nice to know of any concrete performance stats that occur from compiler optimization.

    When releasing with optimise+ and pdbonly, does the runtime automatically load the PDBs? Or only when exceptions occur and debugging?

  2. Thanks for the article .

  3. Thanks for article..

    I realize the IL is optimized, but what about JIT time? Also how about the working set, just seems like this has to have some performance price, no?

  4. güzel davetiye sözleri ve davetiye modelleri

  5. optimize- /debug:pdbonly in VS2008, .net 3.5 is still optimized at runtime


Leave a comment

No trackbacks yet.