Dealing with 32-bit, 64-bit and Any CPU compilation options in .NET

Dealing with 32-bit, 64-bit and Any CPU compilation options in .NET

Ever wondered why Visual Studio IDE provides 3 options for compilation - x86, x64 and Any CPU? While most are aware of these options, many still lack clarity on the significance of these options (including me earlier in my career). So, as part of this article, I hope to clear some of these doubts.

Windows OS has evolved over the years, right from the time it started as a 16-bit OS, then transitioned to 32-bit (x86) and more recently to 64-bit (x64).

Why would you use a 64-bit OS over a 32-bit? To understand this, let's understand the capabilities of both 32-bit and 64-bit CPUs.

  • 32-bit CPU

    • Address pointer size is 32 bits -> can access 2^32 (4,294,967,296) discrete addresses.

    • This allows a program to make a data structure in memory up to 4 GB in size.

  • 64 bit CPU

    • Address pointer size is 64 bits -> can access 2^64 (18,446,744,073,709,551,616) discrete addresses.

    • This allows a program to make a data structure in memory up to 16 Exabytes in size.

  • Main advantages (64-bit over 32-bit)

    • A process on a 64-bit CPU can work with a larger set of data compared to a 32-bit CPU (only constrained by physical memory).

    • A 64-bit integer makes arithmetic or logical operations using 64-bit types such as C#'s long faster than one implemented as two 32-bit operations.

  • To summarize, for all practical purposes...

    • Applications that use large amounts of memory, like Image and video editing software, 3D rendering utilities, and video games will make better use of a 64-bit architecture and operating system, especially if the machine has 8 or even 16 GB of RAM that can be divided among the applications that need it.

Understanding the behavior of 32-bit, 64-bit and Any CPU-compiled PE files:

  • On a 32-bit machine:

    • Any CPU: runs as a 32-bit process, can load Any CPU and x86 assemblies will get BadImageFormatException if it tries to load an x64 assembly.

    • Any CPU-32-bit preferred (default): same as Any CPU.

    • x86: same as Any CPU.

    • x64: BadImageFormatException always.

  • On a 64-bit machine:

    • Any CPU: runs as a 64-bit process, can load Any CPU and x64 assemblies will get BadImageFormatException if it tries to load an x86 assembly.

    • Any CPU-32-bit preferred (default): runs as a 32-bit process, can load Any CPU and x86 assemblies will get BadImageFormatException if it tries to load an x64 assembly.

    • x86: runs as a 32-bit process, can load Any CPU and x86 assemblies will get BadImageFormatException if it tries to load an x64 assembly.

    • x64: same as Any CPU.

Any CPU-32-bit is preferred This is a new option introduced starting with .NET 4.5 and Visual Studio 11. It is a new subtype of Any CPU and is also the default compilation option in the IDE.

You can check this in the project properties as shown in the screenshot below ...

How do 32-bit PEs work on 64-bit architectures?

  • Generally, only 32-bit PEs can be loaded into a 32-bit process, and only 64-bit PEs can be loaded into a 64-bit process.

  • When a 32-bit PE is launched on a 64-bit OS, it runs in the "WOW” (Windows-32 on Windows-64) to present an illusion of a 32-bit operating system to the process.

Why was the Any CPU compilation option introduced?

  • Now before 64-bit Windows OS was introduced there were just the 32-bit PEs (of course, we are not considering 16-bit OS in this discussion) and hence during compilation these PEs were marked as 32-bit and there was no ambiguity.

  • With the introduction of 64-bit Windows OS, a decision had to be made - should the binaries be marked as 32-bit or 64-bit by default?

  • Technically speaking, managed binaries have no hard CPU dependency - they could be either 32 or 64-bit.

  • However, there was a way required to reuse .Net libraries from both 32-bit and 64-bit processes. For this, the OS loader support was extended to support architecture-neutral PE files (Any CPU).

  • In the case of Any CPU PEs the OS loader decides how to initialize the process. On 64-bit OS they are run as 64-bit processes, and on 32-bit OS they are run as 32-bit processes.

Hope this post helped clarify a few things!! I'll also touch upon how to identify the architecture affinity of a PE in a future post.

Abbreviations:

PE: Process Executables (exe's and dll's)

References