.NET

64-Bit SIMD Code from C#

A new beta of the CLR's just-in-time compiler enables direct generation of SIMD instructions in 64-bit code. Getting it set up and working takes a little know-how, however.

After much pleading from C# developers, Microsoft has finally introduced a new .NET JIT compiler, codenamed RyuJIT, that adds support for Single Instruction Multiple Data (SIMD) operations with vector types when compiling for x64. In this article, the first in a two-part series, I provide all the necessary steps to install and configure the new JIT compiler and additional packages to generate SIMD instructions from C# code.

The New JIT Compiler for .NET

A few years ago, I wrote a brief post explaining why high-level programming languages such as C# needed to allow developers to take advantage of SIMD instructions. Before RyuJIT, if you wanted to use SIMD instructions in C# without using libraries written in other programming languages such as C++, you had to work with Mono, which provided the Mono.Simd namespace since its 2.2 release.

RyuJIT is the codename for a new 64-bit JIT compiler for .NET. The codename is the result of joining the words Ryu (Ryū Japanese for dragon) and JIT. If you want to know why a dragon is closely related to a compiler, consult the Dragon Book. RyuJIT provides many optimizations in the generated x64 code (compared with the existing JIT compilation) that makes .NET applications start and run faster. One of these features is the ability to emit SIMD instructions.

At the time of writing, the latest version of RyuJIT is Community Technology Preview 4, also known as RyuJIT CTP4. The initial RyuJIT CTP versions worked only with 64-bit editions of Windows 8.1 or Windows Server 2012 R2. Luckily, RyuJIT CTP4 added support for Windows Vista, 7, 8, and Windows Server 2008/2008R2. However, if you want to work with operating systems supported by the newest version, you need to install Microsoft .NET Framework 4.5.2.

RyuJIT is capable of emitting SIMD instructions included in the Streaming SIMD Extensions 2 (SSE2) instruction set. To support the generation of more powerful SIMD instruction sets, such as Advanced Vector Extensions (AVX), there are additional changes that should be included in a future .NET Framework release. According to the information provided by the RyuJIT team, the final release will be able to generate AVX instructions.

If you install and activate RyuJIT, the Microsoft SIMD-enabled vector types will permit you to generate SIMD instructions by using the vector types included in the System.Numerics namespace. Unfortunately, these steps are more complex than you might expect, so I'm going to provide detailed instructions.

Installing and Activating RyuJIT

You can install RyuJIT without generating undesired side effects in your existing Visual Studio 2013 and .NET Framework installations. There is a mechanism to activate and deactivate the RyuJIT,so you can easily go back to the original JIT, if you want to.

First, if you aren't working with either Windows 8.1 or Windows Server 2012 R2, don't forget that you must install Microsoft .NET Framework 4.5.2. Then, download the RyuJIT installer (RyuJIT-CPT4.msi) from http://aka.ms/RyuJIT and execute it. Once you finish installing RyuJIT, the managed 64-bit applications will still continue using the previous JIT because you have to activate RyuJIT.

You can run a few commands to activate RyuJIT and enable SIMD instructions per user, or set temporary environment variables to activate these features in the command line before you run an executable. If you decide to activate RyuJIT per user, all managed 64-bit applications will use RyuJIT while it's activated.

You need to execute the following commands to add the necessary registry entries that activate RyuJIT and enable SIMD instructions in .NET Framework for the current user:

If you just want to activate RyuJIT and enable SIMD instructions in .NET Framework before running an executable at the command line, you can run the following commands to set the temporary environment variables and avoid changes to the registry.

SET COMPLUS_AltJit=*
SET COMPLUS_FeaturesSIMD=1

Adding SIMD-enabled Vector Types to a Project

It is necessary to add some magic code to make SIMD-enabled vector types work as expected. I'll provide an example with a WPF application.

Create a new Visual C# Windows WPF application project in Visual Studio 2013. Then, run the following command within the Package Manager Console. The -Pre option indicates that you want to install a pre-release package. At the time of writing, the latest version for Microsoft.Bcl.Simd is 1.0.2-beta. You can check the latest version and the versions history for this NuGet package here.

Install-Package Microsoft.Bcl.Simd -Pre

If you check the References for the project within Solution Explorer, you will notice the installation added the System.Numerics.Vectors assembly. If you work with Windows 8.x, the path for this assembly will be packages\Microsoft.Bcl.Simd.1.0.2-beta\lib\portable-net45+win8\System.Numerics.Vectors.dll within your solution's base folder. The installation also adds a new packages.config file to the project with the following lines:

By default, when a debugger is attached, there is a setting in Visual Studio 2013 that makes the JIT compiler suppress all the optimizations. Thus, if you write code that is supposed to generate SIMD instructions with RyuJIT and compile it in release mode with the default debugging settings, the JIT compiler won't generate SIMD instructions. To debug a binary with SIMD intrinsics select Tools | Options… | Debugging | General and uncheck the Suppress JIT optimization on module load (Managed only) checkbox.

The RyuJIT CTP4 version requires some magic code to get SIMD generation working. You need to use the System.Numerics.Vector type in the class constructor. This magic shouldn't be necessary in the final release. The following lines show the new code for the App class (App.xaml.cs) that declares a static System.Numerics.Vector<float> named dummy, and then initializes it in the class constructor with System.Numerics.Vector<float>.One. If you peek at the assembly code generated for this initialization, you should take into account that, in this case, the generated code won't be optimized to take full advantage of SIMD instructions. I know, you might be wondering why such a thing is necessary, but it won't work correctly if you don't add the code.

The System.Numerics.VectorMath class includes math functions to operate on generic and static type vectors. Each math operation generates SIMD intrinsics with RyuJIT. However, the math functions will also work OK when the JIT compiler isn't generating the optimized SIMD intrinsics. Thus, its best to check the boolean value of the System.Numerics.VectorMath.IsHardwareAccelerated property. A true value indicates that all the necessary configurations and magic code are in place to generate the optimized SIMD intrinsics. The previous lines check the value for this property and exit if the value is false. If the value is false, make sure you followed all the earlier steps to activate RyuJIT and enable SIMD instructions.

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task.
However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

Video

This month's Dr. Dobb's Journal

This month,
Dr. Dobb's Journal is devoted to mobile programming. We introduce you to Apple's new Swift programming language, discuss the perils of being the third-most-popular mobile platform, revisit SQLite on Android
, and much more!