-
Book Overview & Buying
-
Table Of Contents
High-Performance Programming in C# and .NET
By :
When .NET code is compiled, it is compiled into Microsoft Intermediate Language (MSIL). MSIL gets interpreted by a JIT compiler when it is needed. The JIT compiler then compiles the necessary MSIL code into native binary code. Subsequent calls to the same code call the binary version of the code, not the MSIL version of the code. This means that MSIL code is always slower than native code, as it is compiled to native on the first run.
JIT code has the advantage of being cross-platform code at the expense of longer startup times. The code of an MSIL assembly that runs is compiled to native code by the JIT compiler. The native code is optimized by the JIT compiler for the target hardware it is running on.
By default, UWP applications are compiled to native code using .NET Native, while iOS applications are compiled to native code via Xamarin/Xamarin.Forms. Microsoft .NET Core can also be compiled into native code.
When using dotnet to compile an assembly to native code, you will need to specify a target framework. For a list of supported target frameworks, please refer to https://docs.microsoft.com/en-us/dotnet/standard/frameworks. You will also need to specify a Runtime Identifier (RID). For a list of supported RIDs, please refer to https://docs.microsoft.com/en-us/dotnet/core/rid-catalog.
Note
At the time of writing, native compilation against .NET 5.0 does have its issues. So, to keep things simple, we will demonstrate native compilation into a single executable against netcoreapp3.1 and win10-x64.
To demonstrate the compilation of Microsoft .NET Core applications into natively compiled single executables, we will write a simple demonstration application that traverses a directory structure and converts audio files from one format into another:
ffmpeg for your operating system. Mine is Windows 10.ffmpeg files into the C:\Tools\ffmpeg folder. Add the following using statements to the top of the Program.cs file:using System; using System.Diagnostics; using System.IO;
using statements listed will help us debug our code and perform I/O on the filesystem. Now, at the top of the Program class, add the following three fields:private static string _baseDirectory = string.Empty; private static string _sourceExtension = string.Empty; private static string _destinationExtension = string .Empty;
BaseDirectory member holds the starting directory that will be processed. sourceExtension holds the extension of the file type, such as .wav, we are after converting to, while destinationExtension holds the extension, such as .ogg, of the file type we are after converting to. Update your Main method so that it looks as follows:static void Main(string[] args)
{
Console.Write("Enter Source Directory: ");
_baseDirectory = Console.ReadLine();
Console.Write("Enter Source Extension: ");
_sourceExtension = Console.ReadLine();
Console.Write("Enter Destination Extension: ");
_destinationExtension = Console.ReadLine();
new Program().BatchConvert();
}Main method, we have requested that the user enters the source directory, source extension, and destination extension. Then, we set out member variables and called the BatchConvert method. Let's add our BatchConvert method:private void BatchConvert()
{
var directory = new DirectoryInfo(_baseDirectory);
ProcessFolder(directory);
}BatchConvert method creates a new DirectoryInfo object called directory and then passes the directory object into the ProcessFolder method. Let's add this method now:private void ProcessFolder(DirectoryInfo
directoryInfo)
{
Console.WriteLine($"Processing Directory:
{directoryInfo.FullName}");
var fileInfos = directoryInfo.EnumerateFiles();
var directorieInfos = directoryInfo.
EnumerateDirectories();
foreach (var fileInfo in fileInfos)
if (fileInfo.Extension.Replace(".", "")
== sourceExtension)
ConvertFile(fileInfo);
foreach (var dirInfo in directorieInfos)
ProcessFolder(dirInfo);
}ProcessFolder method outputs a message to the screen so that the user knows what folder is being processed. Then, it obtains an enumeration of the FileInfo and DirectoryInfo objects from the directoryInfo parameter. After this, it converts all the files in that folder that have the required source file extension. Once all the files have been processed, each of the DirectoryInfo objects is processed by calling the ProcessFolder method recursively. Finally, let's add our ConvertFile method:private void ConvertFile(FileInfo fileInfo)
{
}ConvertFile method takes a FileInfo parameter. This parameter contains the file that is to undergo conversion. The remaining code will be added to this ConvertFile method. Add the following three variables:var timeout = 10000;
var source = $"\"{fileInfo.FullName}\"";
var destination = $"\"{fileInfo.FullName.Replace
(_sourceExtension, _destinationExtension)}\"";timeout variable is set to 10 seconds. This gives the process 10 seconds to process each file. The source variable contains the full name of the file to be converted, while the destination variable contains the full path of the newly converted file. Now, add the check to see if the converted file exists:if (File.Exists(fileInfo.FullName.Replace
(_sourceExtension, _destinationExtension)))
{
Console.WriteLine($"Unprocessed: {fileInfo.FullName}");
return;
}destination file exists, then the conversion has already taken place, so we do not need to process the file. So, let's output a message to the user to inform them that the file is unprocessed, and then return from the method. Let's add the code to perform the conversion:Console.WriteLine($"Converting file: {fileInfo.FullName}
from {_sourceExtension} to {_destination
Extension}.");
using var ffmpeg = new Process
{
StartInfo = {
FileName = @"C:\Tools\ffmpeg\bin
\ffmpeg.exe",
Arguments = $"-i {source}
{destination}",
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
CreateNoWindow = true
}
};
ffmpeg.EnableRaisingEvents = false;
ffmpeg.OutputDataReceived += (s, e) => Debug.WriteLine
($"Debug: e.Data");
ffmpeg.ErrorDataReceived += (s, e) => Debug.WriteLine
($@"Error: {e.Data}");
ffmpeg.Start();
ffmpeg.BeginOutputReadLine();
ffmpeg.BeginErrorReadLine();
ffmpeg.WaitForExit(timeout);ffmpeg.exe and converts an audio file from one format into another, as specified by the user. The converted file is then saved in the same directory as the original file..wav file format. However, they need to be transformed into .ogg files to be used in an Android program that I use. You can use your own audio file or music folders.Note
If you don't have any audio files to hand to test this small program, you can download some royalty-free sounds from https://www.bensound.com. You can check the following page for links to various public music domains: https://www.lifewire.com/public-domain-music-3482603.
Figure 1.12 – Our file converter showing the directory and file conversion formats
The program will now process all files and folders under the specified parent folder and process them.
The program is working as expected in its MSIL form. However, we can see the delay in performing the file conversions. Let's compile our file converter into a single native executable, and then see if it is visibly any faster:
TargetFramework property of the project should also be updated to netcoreapp3.1; otherwise, this may not work – that is, if it is set to net5.0. Type the following command and then press Enter:dotnet publish --framework netcoreapp3.1 - p:PublishSingleFile=true --runtime win10-x64
Figure 1.13 – The Developer Command Prompt in administrative mode showing the native compilation output
Figure 1.14 – Windows Explorer displaying the output files resulting from native compilation
CH01_NativeCompilation.exe file. You will see that .wav files are processed into .ogg files much quicker.In this section, we learned how to write a console app. We compile the console app to MSIL and then compile the console app into a single native executable file. Visually, from the user's perspective, the file processes batch audio files much quicker in native form than in MSIL form.
Now, let's learn how to improve Windows Store applications.