Code Optimizations Explained. Part 1

Eazfuscator.NET 3.7 brings new code optimizations: constant propagation and variable reordering. Sounds cool but what does it all mean?

We kept it in secret in the past. Now is the time to make it a public knowledge.

Every code optimization technique is marked with an identifier (like EFOPT-0001) for the sake of order and bookkeeping purposes.

Let's see what Eazfuscator.NET does with your code beyond the obfuscation.

EFOPT-0001: LINQ Any() substitution

LINQ (Language Integrated Query) is a .NET component that adds native data querying capabilities to .NET languages. LINQ syntax and operations are fluent and handy. Unfortunately they are not as fast as their imperative equivalents.

Eazfuscator.NET does its best to improve this. Let's see how it optimizes Any() operation:

Before optimization:

static void Main(string[] args)
{
    if (args.Any())
        Console.WriteLine("Program has arguments.");
}

After optimization:

static void Main(string[] args)
{
    if (args.Length != 0)
        Console.WriteLine("Program has arguments.");
}

It is worth to mention that not all Any() operations can be safely substituted. To avoid undesirable side effects, Eazfuscator.NET carefully tracks the data flow. All suitable LINQ Any() operations are then changed to their imperative analogues during optimization for a great benefit: a faster code execution.

Imperative code is in average 5 times faster than corresponding LINQ equivalents.

EFOPT-0003: Enum.HasFlag() substitution

Let’s take a look at example.

Example 1. The slow code

[Flags]
enum RunOptions
{ 
    None = 0x00,
    PrepareDatabase = 0x01,
    SkipPlugins = 0x02
}

class Engine
{
    public void Run(RunOptions options)
    {
        if (options.HasFlag(RunOptions.PrepareDatabase))
            InitializeDatabase();
        // ...
    }

    // ...
}

The code above uses Enum.HasFlag method to check whether PrepareDatabase flag is set. Being sweet in syntax, the code has astonishingly bad performance due to boxing operations that are invisibly generated by C# compiler.

Example 2. The fast code. Produced by Eazfuscator.NET after optimizing the slow code

public void Run(RunOptions options)
{
    if ((options & RunOptions.PrepareDatabase) == RunOptions.PrepareDatabase)
        InitializeDatabase();
    // ...
}

As you can see, Eazfuscator.NET emitted a functionally equivalent code. The result of optimization is 500x speed improvement of condition evaluation over original slow code.

EFOPT-0004: String length folding

Strings are extremely widespread primitives in applications. They are close to human thinking and to human speech. No wonder System.String is a built-in .NET type.

Eazfuscator.NET applies a bunch of optimizations to string operations. One of them is string length folding.

Folding is a bit vague term however it is very common in optimization theory slang. Folding literally means precalculation in terms of optimization. Let's see how it works:

Before optimization:

static void Main()
{
    string s = "Optimization makes a difference.";
    Console.WriteLine(s.Length);
}

After optimization:

static void Main()
{
    Console.WriteLine(32);
}

As you can see, s.Length value is extracted and string access is eliminated during optimization. Benefits: less code and more speed.

EFOPT-0005: Elimination of redundant IDisposable null check

C#, VB.NET and other .NET languages provide using statement. It allows to have a scoped access to a given resource:

Example 1. Scoped access to a file

static void Main()
{
    using (var file = new FileStream(@"C:\Sample.txt", FileMode.Open))
    {
        Console.WriteLine(file.ReadByte());
    }
}

using statement is just a syntatic sugar. It gets translated behind the scenes by C# compiler to the following code:

Example 2. Scoped access under the hood

static void Main()
{
    var file = new FileStream(@"C:\Sample.txt", FileMode.Open);
    try
    {
        Console.WriteLine(file.ReadByte());
    }
    finally
    {
        if (file != null)
            file.Dispose();
    }
}

See that marked line in the listing above? That line is redundant. The value of file variable will never get null at that point, so it is safe to call Dispose method without prior null check. C# compiler could be smarter here.

Eazfuscator.NET does the job:

Example 3. Optimized scoped access

static void Main()
{
    var file = new FileStream(@"C:\Sample.txt", FileMode.Open);
    try
    {
        Console.WriteLine(file.ReadByte());
    }
    finally
    {
        file.Dispose();
    }
}

A redundant null check is pruned during optimization. Benefits: less code and more speed.

EFOPT-0015: NOP instruction removal

NOP is an IL instruction. It transliterates as "no operation" and it actually does nothing. A perfect target for optimization.

You can not emit NOPs explicitly from such high-level languages as C# and VB.NET. However, .NET compilers produce them from time to time. Eazfuscator.NET optimizer comes to the rescue:

Before optimization:

.method static void Main()
{
    .entrypoint
    .maxstack 1
    ldstr "Hello, world!"
    nop
    call void [mscorlib]System.Console::WriteLine(string)
    ret
}

After optimization:

.method static void Main()
{
    .entrypoint
    .maxstack 1
    ldstr "Hello, world!"
    call void [mscorlib]System.Console::WriteLine(string)
    ret
}

NOP instruction is removed during optimization. Benefits: less code and more speed.


That's all for now. The article is going to be big and everything big tends to be boring and lacks fun. That's why I'm splitting the topic into several parts.

Thanks for reading and stay tuned!

Download Eazfuscator.NET and see yourself how it boosts the throughput of your code.

comments powered by Disqus