Dot Net Perls

ToString Use With Values - C#

by Sam Allen

Problem

Compare approaches of using ToString on ints. There are performance and functional differences with the NumberFormat on integers. Construct best-practices by inspecting the IL code generated.

Solution: C#

What does MSDN say about the ToString method on integers? To answer this I looked at this page at msdn.microsoft.com.

What MSDN says. ToString returns a human-readable string that is culture-sensitive. For example, for an instance of the Double class whose value is zero, the implementation of Double.ToString might return "0.00" or "0,00" depending on the current UI culture.

Examples of using ToString()

How does ToString() work? Here I compare ToString() with no parameters (no format string) and ToString() with the NumberFormatInfo specified. ToString() can receive a System.Globalization. NumberFormatInfo parameter.

using System;
using System.Globalization; // Important

class Program
{
    static void Main()
    {
        int a = 4000;
        int b = 654;
        double c = 453.4;
        double d = 50000.55555;

        string a1 = a.ToString();
        string a2 = a.ToString(NumberFormatInfo.InvariantInfo);
        Console.WriteLine(a1 + " " + a2);
        // 4000 4000

        string b1 = b.ToString();
        string b2 = b.ToString(NumberFormatInfo.InvariantInfo);
        Console.WriteLine(b1 + " " + b2);
        // 654 654

        string c1 = c.ToString();
        string c2 = c.ToString(NumberFormatInfo.InvariantInfo);
        Console.WriteLine(c1 + " " + c2);
        // 453.4 453.4

        string d1 = d.ToString();
        string d2 = d.ToString(NumberFormatInfo.InvariantInfo);
        Console.WriteLine(d1 + " " + d2);
        // 50000.55555 50000.55555
    }
}

Identical results. What we see above is that the ToString() without a parameter (a1, b1, c1, d1) all return the same strings as the overloaded methods with a parameter.

Invariant cultures. What the word invariant means is that the format doesn't vary with the social culture of the machine.

Is your code culture-sensitive?

Mine isn't. I use ToString() in many places simply to convert an integer to a string (5 to "5", for example). What I found when inspecting the intermediate language is that ToString() with no parameters actually gets the culture internally, but this is non-optimal.

What does int.ToString() do internally?

It uses a property called CultureInfo to get the current culture. It then sends the System.IFormatProvider to the ToString() method. So ToString() with no parameters simply gets NumberFormatInfo on its own.

.method public hidebysig instance class System.Text.StringBuilder
    Append(int32 'value') cil managed
{
    .maxstack 8
    L_0000: ldarg.0
    L_0001: ldarga.s 'value'
    L_0003: call class System.Globalization.CultureInfo
            System.Globalization.CultureInfo::get_CurrentCulture()
    L_0008: call instance string System.Int32::ToString(class System.IFormatProvider)
    L_000d: call instance class System.Text.StringBuilder
            System.Text.StringBuilder::Append(string)
    L_0012: ret
}

How can we optimize?

In my research, I have found that property accesses like get_CurrentCulture() above are slow. So, my solution to this inefficiency is to pass ToString() an already-created NumberFormatInfo. Here's how I changed the code.

//
// A.
// This code converts the int 900 to a string.
//
string a = 900.ToString(); // "900"

//
// B.
// This code converts the int 900 to a string.
// Has the same exact results.
//
NumberFormatInfo n = CultureInfo.InvariantCulture.NumberFormat;
string b = 900.ToString(n); // "900"

Why is that different?

B is different because it eliminates the need for ToString() to access a property internally. This is a significant optimization for this operation, particularly in tight loops.

How much faster is it?

Somewhere around 10% for invariant cultures. This can build on my work with StringBuilder mistakes, Split improvements, Response.Write performance, and others.

using System;
using System.Globalization; // important

class Program
{
    static void Main()
    {
        int m = 10000000;
        //
        // Run ToString() benchmark
        //
        long t1 = Environment.TickCount;
        for (int i = 0; i < m; i++)
        {
            string a = i.ToString();
        }
        long t2 = Environment.TickCount;

        //
        // Run ToString(NumberFormatInfo) benchmark
        //
        NumberFormatInfo f = CultureInfo.InvariantCulture.NumberFormat;
        for (int i = 0; i < m; i++)
        {
            string a = i.ToString(f);
        }
        long t3 = Environment.TickCount;

        Console.WriteLine((t2 - t1));
        Console.WriteLine((t3 - t2));
        Console.Read();
    }
}

int.Parse performance improvements

Specifying the NumberFormatInfo on int.Parse() also improves performance. int.Parse uses the same overload logic that int.ToString() does. Here's what you can use for int.Parse().

//
// Use NumberFormatInfo with int.Parse
//
NumberFormatInfo f = CultureInfo.InvariantCulture.NumberFormat;
int i = int.Parse("2000", f); // 2000

Best practices

When using simple integers or other value types, specify the invariant culture number format. This improves the relevant execution time by 10%.

Slow versionFast version
string a = 5.ToString();NumberFormatInfo n = CultureInfo.InvariantCulture.NumberFormat; string a = 5.ToString(n);
int i = "5".ToString();NumberFormatInfo n = CultureInfo.InvariantCulture.NumberFormat; int i = "5".ToString(n);

Summary

Inspecting intermediate language in Reflector (now maintained by Red Gate) is an excellent way to develop best practices and rules for optimization. Here I show a clear way to optimize int.Parse and int.ToString() methods without developing custom algorithms.

Dot Net Perls
About
Sitemap
Source code
RSS
Strings
Split String Examples
IndexOf String Examples
Remove HTML Tags From String
Count Characters in String
Uppercase First Letter in String
Recent
Pi
NGEN Installer Class
List Element Equality
DateTime Tips and Tricks
Remove HTML Tags From String
© 2008 Sam Allen. All rights reserved.