Dot Net Perls

Unsafe Int Parse in C#

by Sam Allen - Updated May 8, 2009

Problem. You want to improve the performance of integer parsing in your C# program, possibly using unsafe code and older C algorithms. Benchmark the improvement, and examine properties of unsafe code in C#. Solution. Here we carefully compare custom and optimized integer parsing with unsafe to the managed framework code.

=== int.Parsing method benchmarks ===

int.Parse method:         89.85 ms 
Unsafe int parse method:  56.78 ms 

1. Using the unsafe keyword

Here we see how you can use the unsafe keyword to optimize some kinds of lower-level code such as parsing routines. You can use a simple algorithm to convert an ASCII string to an integer value, in a way that is far faster than int.Parse.

=== Example unsafe parsing code (C#) ===

using System;

class Program
{
    static void Main()
    {
        string s = "400";

        // A.
        // Control method from framework.
        int i = int.Parse(s);
        Console.WriteLine(i);

        // B.
        // Control method from framework.
        ushort i2 = ushort.Parse(s);
        Console.WriteLine(i2);

        // C.
        // Use unsafe custom method.
        int i3 = ParseUnsafe(s);
        Console.WriteLine(i3);

        // D.
        // Use unsafe custom method.
        ushort i4 = (ushort)ParseUnsafe(s);
        Console.WriteLine(i4);
    }

    /// <summary>
    /// Convert a series of characters to a number using pointers.
    /// </summary>
    unsafe static int ParseUnsafe(string value)
    {
        int result = 0;
        fixed (char* v = value)
        {
            char* str = v;
            while (*str != '\0')
            {
                result = 10 * result + (*str - 48);
                str++;
            }
        }
        return result;
    }
}

=== Output of the program ===

400
400
400
400

Description of the example code. ParseUnsafe above receives a C# string and returns a C# integer that is represented by the string. It has useful XML comments, and is reliable. Look at the keyword fixed used above. It is essential for telling the framework not to destroy the memory when the unsafe code runs. You must use this syntax, and you should assign a char* pointer to the string.

More on pointers. You must assign a new char* to the fixed char pointer. This is because we can't change the first char*. This is caused by how memory is 'pinned' in C#. We increment the str pointer and walk through each character.

Conversion of the chars. It uses a classic C operation to convert a character like '1' to the integer 1. ASCII characters are stored in different positions than the numbers equivalent to them, but in the same order.

2. Performance notes

Think of how many different things int.Parse must deal with: negative numbers, decimals, null characters, letters, line breaks, spaces, colons and different locales. We don't need all that every time. We just need to parse a series of characters and store it in an int or ushort.

Notes on the multiplication. Here is what happens: As we read the string from the left to the right, the current digit is one tenth of the previous one. We can multiply the previous number by 10 to satisfy this condition. The 48 I use simply shifts ASCII digits to ints.

3. Managed code method

You can gain most of the performance advantage here by using purely managed code. The author has not benchmarked this method against the unsafe method, but has measured it as a large performance boost over the int.Parse method. The exact code below is run around every 3 seconds continuously.

/// <summary>
/// Convert simple integers in string to ints.
/// [Note] About 20x faster than int.Parse.
/// </summary>
public static int IntParseFast(string value)
{
    int result = 0;
    for (int i = 0; i < value.Length; i++)
    {
        char letter = value[i];
        result = 10 * result + (letter - 48);
    }
    return result;
}

4. Summary

Here we saw some alternative integer parsing routines, both in unsafe and managed code. Unsafe code can speed up and even simplify your code. It has many risks and should be used carefully. Sometimes you can parse integers with the method here for a performance improvement.

Dot Net Perls
Numbers | Convert Bool to Int | int.Max and Min Constants | int.Parse for Integer Conversion | int.TryParse Method, Parsing... | Random Number Generator
C# | Reflection Field Example | Validate Characters in String | Main Args Examples | Enum String Method
© 2009 Sam Allen. All rights reserved.